Build a Simple Next.js Interactive Web-Based Contact Manager

Written by

in

In today’s fast-paced world, staying organized is key. Managing contacts efficiently is crucial whether you’re a freelancer juggling multiple clients, a small business owner networking with potential partners, or simply someone who wants to keep their personal contacts in order. Manually managing contacts in spreadsheets or relying solely on your phone’s contact list can quickly become cumbersome and inefficient. What if you could build a custom, web-based contact manager tailored to your specific needs? This tutorial will guide you through building a simple, yet functional, contact manager using Next.js, a powerful React framework, offering a seamless and interactive user experience.

Why Build a Contact Manager with Next.js?

Next.js offers several advantages for this project:

  • Server-Side Rendering (SSR) and Static Site Generation (SSG): Improve SEO and initial load times.
  • React-Based: Leverage the power and flexibility of React for building interactive UIs.
  • Routing: Easy to manage routes and navigation.
  • API Routes: Build backend APIs within the same project.
  • Fast Refresh: Experience rapid development with instant updates in the browser.

By the end of this tutorial, you’ll have a fully functional contact manager, allowing you to add, edit, delete, and view contacts. This project is ideal for beginners to intermediate developers looking to enhance their Next.js skills and build practical, real-world applications. Let’s get started!

Prerequisites

Before we begin, make sure you have the following installed:

  • Node.js and npm (or yarn): Required for running JavaScript and managing project dependencies.
  • A code editor: Such as VS Code, Sublime Text, or Atom.
  • Basic knowledge of JavaScript, HTML, and CSS: Familiarity with React is helpful but not strictly necessary.

Setting Up Your Next.js Project

First, let’s create a new Next.js project. Open your terminal and run the following command:

npx create-next-app contact-manager
cd contact-manager

This command creates a new Next.js project named “contact-manager” and navigates you into the project directory. You can use yarn instead of npm if you prefer, e.g., `yarn create next-app contact-manager`.

Project Structure Overview

Let’s take a quick look at the project structure:

  • pages/: This directory contains your application’s pages. Each file in this directory represents a route. For example, `pages/index.js` corresponds to the `/` route, and `pages/about.js` corresponds to the `/about` route.
  • public/: This directory is for static assets like images, fonts, and other files you want to serve directly.
  • styles/: This directory is where you’ll put your CSS or other styling files. Next.js supports CSS Modules, styled-jsx, and other styling solutions.
  • components/: We will create this directory to store reusable React components.
  • package.json: Contains project metadata and dependencies.

Creating the Contact Model

Before building the UI, let’s define the structure of our contact data. We’ll create a simple JavaScript object to represent a contact. Create a new file named `components/Contact.js` and add the following code:

// components/Contact.js

const Contact = {
  id: String,
  firstName: String,
  lastName: String,
  email: String,
  phone: String,
  notes: String,
};

export default Contact;

This defines a contact object with properties for id, first name, last name, email, phone, and any notes. Note that this is a simple type definition and does not include any validation or data storage logic. We’ll handle data storage and retrieval later.

Building the Contact List Component

Now, let’s create a component to display the list of contacts. Create a file named `components/ContactList.js` and add the following code:

// components/ContactList.js
import React from 'react';

function ContactList({ contacts, onDeleteContact, onEditContact }) {
  return (
    <div>
      <h2>Contacts</h2>
      <ul>
        {contacts.map((contact) => (
          <li>
            {contact.firstName} {contact.lastName} - {contact.email}
            <button> onEditContact(contact)}>Edit</button>
            <button> onDeleteContact(contact.id)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default ContactList;

This component takes an array of contacts as a prop and renders a list of contact items. It also includes “Edit” and “Delete” buttons for each contact. The `onDeleteContact` and `onEditContact` props are functions that will be called when the respective buttons are clicked. This is a good practice for passing events up to the parent component for handling the state.

Creating the Contact Form Component

Next, let’s create a form for adding and editing contacts. Create a file named `components/ContactForm.js`:


// components/ContactForm.js
import React, { useState, useEffect } from 'react';

function ContactForm({ initialContact, onSubmit }) {
  const [contact, setContact] = useState(initialContact || {
    firstName: '',
    lastName: '',
    email: '',
    phone: '',
    notes: '',
  });

  useEffect(() => {
    setContact(initialContact || {
      firstName: '',
      lastName: '',
      email: '',
      phone: '',
      notes: '',
    });
  }, [initialContact]);

  const handleChange = (e) => {
    const { name, value } = e.target;
    setContact(prevContact => ({
      ...prevContact,
      [name]: value,
    }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    onSubmit(contact);
    setContact({
      firstName: '',
      lastName: '',
      email: '',
      phone: '',
      notes: '',
    });
  };

  return (
    
      <div>
        <label>First Name:</label>
        
      </div>
      <div>
        <label>Last Name:</label>
        
      </div>
      <div>
        <label>Email:</label>
        
      </div>
      <div>
        <label>Phone:</label>
        
      </div>
      <div>
        <label>Notes:</label>
        <textarea id="notes" name="notes" />
      </div>
      <button type="submit">{initialContact ? 'Update Contact' : 'Add Contact'}</button>
    
  );
}

export default ContactForm;

This component manages the form fields and handles the submission. It uses the `useState` hook to manage the form data and the `useEffect` hook to reset the form when an `initialContact` prop changes, allowing us to use the same form for both adding and editing contacts. The `onSubmit` prop is a function that will be called when the form is submitted.

Building the Main App Component (index.js)

Now, let’s build the main application component, which will orchestrate the other components and manage the contact data. Open `pages/index.js` and replace the existing code with the following:


// pages/index.js
import React, { useState, useEffect } from 'react';
import ContactList from '../components/ContactList';
import ContactForm from '../components/ContactForm';

function HomePage() {
  const [contacts, setContacts] = useState([]);
  const [editingContact, setEditingContact] = useState(null);

  useEffect(() => {
    // Load contacts from local storage on component mount
    const storedContacts = localStorage.getItem('contacts');
    if (storedContacts) {
      setContacts(JSON.parse(storedContacts));
    }
  }, []);

  useEffect(() => {
    // Save contacts to local storage whenever the contacts state changes
    localStorage.setItem('contacts', JSON.stringify(contacts));
  }, [contacts]);

  const handleAddContact = (newContact) => {
    const id = Date.now().toString(); // Generate a unique ID
    const contactWithId = { ...newContact, id };
    setContacts([...contacts, contactWithId]);
  };

  const handleDeleteContact = (id) => {
    setContacts(contacts.filter((contact) => contact.id !== id));
  };

  const handleEditContact = (contact) => {
    setEditingContact(contact);
  };

  const handleUpdateContact = (updatedContact) => {
    setContacts(contacts.map((contact) => (contact.id === updatedContact.id ? updatedContact : contact)));
    setEditingContact(null);
  };

  return (
    <div>
      <h1>Contact Manager</h1>
      
      
    </div>
  );
}

export default HomePage;

This is the core of our application. It does the following:

  • Manages State: Uses `useState` to manage the list of contacts (`contacts`) and the contact being edited (`editingContact`).
  • Loads and Saves Data: Uses `useEffect` to load contacts from local storage on component mount and save them whenever the `contacts` state changes.
  • Handles Events: Defines functions to add, delete, edit, and update contacts.
  • Renders Components: Renders the `ContactForm` (passing `editingContact` as a prop) and `ContactList` components, passing the appropriate props and event handlers.

Styling the Application (Optional)

To make the application look better, you can add some basic styling. Open `styles/globals.css` and add the following CSS:


/* styles/globals.css */
body {
  font-family: sans-serif;
  margin: 20px;
}

h1 {
  margin-bottom: 20px;
}

form {
  margin-bottom: 20px;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
}

label {
  display: block;
  margin-bottom: 5px;
}

input[type="text"], input[type="email"], input[type="tel"], textarea {
  width: 100%;
  padding: 8px;
  margin-bottom: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-sizing: border-box;
}

button {
  background-color: #4CAF50;
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  margin-right: 10px;
}

button:hover {
  background-color: #3e8e41;
}

ul {
  list-style: none;
  padding: 0;
}

li {
  padding: 10px;
  border: 1px solid #eee;
  margin-bottom: 5px;
  border-radius: 4px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

Feel free to customize the styles to your liking. You can use CSS Modules, styled-components, or any other styling solution supported by Next.js.

Running Your Application

Now, start your development server by running the following command in your terminal:

npm run dev
# or
yarn dev

Open your browser and go to `http://localhost:3000`. You should see your contact manager application. You can now add, edit, and delete contacts. The data will be stored in your browser’s local storage.

Common Mistakes and How to Fix Them

Here are some common mistakes and how to avoid them:

  • Incorrect Import Paths: Double-check your import paths in your components. Make sure they accurately reflect the location of your files.
  • State Management Issues: Ensure your state is being updated correctly. Use the correct state update functions (e.g., `setContacts`) and avoid directly modifying the state. If you are having issues with data not updating, be sure to check your dependencies in the `useEffect` hook.
  • Local Storage Limitations: Local storage has a limited storage capacity. Consider using a more robust data storage solution (like a database) for larger applications.
  • Unnecessary Re-renders: Optimize your components to prevent unnecessary re-renders. Use `React.memo` for functional components or `shouldComponentUpdate` for class components.
  • Missing Keys in Lists: When rendering lists of elements, always provide a unique `key` prop to each element to help React efficiently update the DOM.

Enhancements and Next Steps

This is a basic contact manager. You can enhance it in several ways:

  • Add Validation: Validate the form fields to ensure data integrity.
  • Implement Search Functionality: Add a search bar to easily find contacts.
  • Integrate a Database: Use a database (e.g., MongoDB, PostgreSQL) to store and retrieve contact data. This will allow you to store more contacts and persist data across sessions.
  • Add Contact Groups/Categories: Allow users to categorize contacts.
  • Implement Pagination: If you have many contacts, implement pagination to improve performance.
  • Improve UI/UX: Enhance the styling and user interface for a better user experience.
  • Add Sorting: Allow users to sort contacts by name, email, etc.

Key Takeaways

This tutorial demonstrated how to build a simple contact manager using Next.js. You learned how to create components, manage state, handle user input, and store data using local storage. This project provides a solid foundation for building more complex web applications with Next.js. Remember to break down your project into smaller, manageable components, test your code frequently, and always prioritize a good user experience.

Building a contact manager, even a simple one, provides a valuable opportunity to practice core web development skills. You’ve gained hands-on experience with state management, component composition, and data persistence. Furthermore, you’ve learned to structure a Next.js application, which is a key skill for any modern web developer. As you continue to build projects, keep experimenting with new features and improvements. The more you code, the more comfortable and proficient you will become. Embrace the learning process, and don’t be afraid to experiment and try new things. Happy coding!