Build a Simple Next.js Interactive Web-Based Bookmarker

Written by

in

In the digital age, we’re constantly bombarded with information, and finding ways to efficiently manage and revisit valuable online resources is crucial. Imagine you’re researching a complex topic, or simply curating a collection of interesting articles. Manually copying and pasting URLs into a document or relying solely on browser bookmarks can quickly become unwieldy. This is where a dedicated bookmarking tool shines. In this tutorial, we’ll build a simple, interactive web-based bookmarking application using Next.js, empowering you to save, organize, and access your favorite web pages with ease.

Why Build a Bookmarker App?

Beyond the convenience of organized web links, building a bookmarking application offers several benefits:

  • Improved Productivity: Quickly access frequently visited websites without manually typing URLs or sifting through browser history.
  • Enhanced Organization: Categorize and tag bookmarks for efficient retrieval.
  • Learning Next.js: Hands-on experience with a modern React framework.
  • Portfolio Project: A practical project to showcase your skills to potential employers.

This tutorial is designed for developers with a basic understanding of HTML, CSS, and JavaScript, and a beginner-to-intermediate level of experience with React. We’ll break down the process into manageable steps, explaining each concept in detail and providing clear, commented code examples. By the end, you’ll have a fully functional bookmarking application and a solid understanding of key Next.js features.

Setting Up Your Next.js Project

Let’s start by creating a new Next.js project. Open your terminal and run the following command:

npx create-next-app bookmark-app

This command will create a new directory called `bookmark-app` and initialize a Next.js project inside it. Navigate into the project directory:

cd bookmark-app

Next, start the development server:

npm run dev

Open your browser and go to `http://localhost:3000`. You should see the default Next.js welcome page. This confirms that your project is set up correctly.

Project Structure and Key Files

Before we dive into the code, let’s familiarize ourselves with the project structure. The key files we’ll be working with are:

  • `pages/index.js`: This file defines the homepage of our application. We’ll modify this to display and manage bookmarks.
  • `components/BookmarkForm.js`: (We’ll create this) A component for adding new bookmarks.
  • `components/BookmarkList.js`: (We’ll create this) A component to display the list of bookmarks.
  • `styles/globals.css`: (Optional) We can use this file for global styling.

Creating the Bookmark Form Component

Let’s create the `BookmarkForm` component. This component will contain a form with an input field for the URL and a button to submit the bookmark. Create a new file named `components/BookmarkForm.js` and add the following code:

import React, { useState } from 'react';

function BookmarkForm({ onAddBookmark }) {
  const [url, setUrl] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    if (url.trim() === '') {
      return; // Prevent empty submissions
    }
    onAddBookmark(url);
    setUrl(''); // Clear the input field
  };

  return (
    <form onSubmit={handleSubmit} className="mb-4">
      <label htmlFor="url" className="block text-gray-700 text-sm font-bold mb-2">URL:</label>
      <input
        type="url"
        id="url"
        value={url}
        onChange={(e) => setUrl(e.target.value)}
        className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
        placeholder="Enter URL"
        required
      />
      <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 Bookmark
      </button>
    </form>
  );
}

export default BookmarkForm;

Explanation:

  • We import `useState` from React to manage the input field’s value.
  • `url` state variable stores the URL entered by the user.
  • `handleSubmit` function is called when the form is submitted. It prevents the default form submission behavior, calls the `onAddBookmark` prop function (which we’ll define later in `pages/index.js`), and clears the input field.
  • The form includes an input field for the URL and a submit button.
  • The `onAddBookmark` prop is a function passed from the parent component (`pages/index.js`) to handle adding new bookmarks.

Creating the Bookmark List Component

Now, let’s create the `BookmarkList` component, which will display the list of bookmarks. Create a new file named `components/BookmarkList.js` and add the following code:

import React from 'react';

function BookmarkList({ bookmarks, onDeleteBookmark }) {
  return (
    <ul>
      {bookmarks.map((bookmark, index) => (
        <li key={index} className="flex items-center justify-between py-2 border-b border-gray-200">
          <a href={bookmark} target="_blank" rel="noopener noreferrer" className="text-blue-500 hover:underline">
            {bookmark}
          </a>
          <button
            onClick={() => onDeleteBookmark(index)}
            className="bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-2 rounded focus:outline-none focus:shadow-outline"
          >
            Delete
          </button>
        </li>
      ))}
    </ul>
  );
}

export default BookmarkList;

Explanation:

  • The component receives two props: `bookmarks` (an array of URLs) and `onDeleteBookmark` (a function to delete a bookmark).
  • It iterates over the `bookmarks` array using the `map` function to render a list item (`<li>`) for each bookmark.
  • Each list item contains an `<a>` tag (a link) that opens the bookmark URL in a new tab. The `target=”_blank” rel=”noopener noreferrer”` attributes are used for security best practices when opening external links.
  • Each list item also includes a delete button that calls the `onDeleteBookmark` function when clicked.

Implementing the Bookmark Management Logic in `pages/index.js`

Now, let’s modify the `pages/index.js` file to integrate the `BookmarkForm` and `BookmarkList` components and manage the bookmarks. Open `pages/index.js` and replace its content with the following code:

import React, { useState, useEffect } from 'react';
import BookmarkForm from '../components/BookmarkForm';
import BookmarkList from '../components/BookmarkList';

function Home() {
  const [bookmarks, setBookmarks] = useState([]);

  // Load bookmarks from local storage on component mount
  useEffect(() => {
    if (typeof window !== 'undefined') {
      const storedBookmarks = localStorage.getItem('bookmarks');
      if (storedBookmarks) {
        setBookmarks(JSON.parse(storedBookmarks));
      }
    }
  }, []);

  // Save bookmarks to local storage whenever the bookmarks state changes
  useEffect(() => {
    if (typeof window !== 'undefined') {
      localStorage.setItem('bookmarks', JSON.stringify(bookmarks));
    }
  }, [bookmarks]);

  const handleAddBookmark = (url) => {
    setBookmarks([...bookmarks, url]);
  };

  const handleDeleteBookmark = (index) => {
    const newBookmarks = [...bookmarks];
    newBookmarks.splice(index, 1);
    setBookmarks(newBookmarks);
  };

  return (
    <div className="container mx-auto p-4">
      <h1 className="text-2xl font-bold mb-4">My Bookmarks</h1>
      <BookmarkForm onAddBookmark={handleAddBookmark} />
      <BookmarkList bookmarks={bookmarks} onDeleteBookmark={handleDeleteBookmark} />
    </div>
  );
}

export default Home;

Explanation:

  • We import `useState` and `useEffect` from React, along with the `BookmarkForm` and `BookmarkList` components.
  • `bookmarks` state variable stores an array of bookmark URLs.
  • `useEffect` Hook (Loading Bookmarks): The first `useEffect` hook is used to load bookmarks from local storage when the component mounts. This ensures that the bookmarks persist even if the user refreshes the page. The `typeof window !== ‘undefined’` check is crucial because Next.js pre-renders pages on the server, where `window` is not available.
  • `useEffect` Hook (Saving Bookmarks): The second `useEffect` hook saves the `bookmarks` state to local storage whenever the `bookmarks` array changes. This keeps our bookmarks persistent.
  • `handleAddBookmark` function adds a new URL to the `bookmarks` array.
  • `handleDeleteBookmark` function removes a bookmark from the `bookmarks` array based on its index.
  • The component renders a heading, the `BookmarkForm` component (passing the `handleAddBookmark` function as a prop), and the `BookmarkList` component (passing the `bookmarks` array and the `handleDeleteBookmark` function as props).

Styling (Optional)

For basic styling, you can use the built-in CSS modules or integrate a CSS framework like Tailwind CSS (as used in the code examples). Here’s how you can add some basic styles using Tailwind CSS. If you did not install Tailwind during project setup, you can do so now. First, install it:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Add the following directives to your `styles/globals.css` file:

@tailwind base;
@tailwind components;
@tailwind utilities;

Then, modify the components’ class names to match Tailwind CSS utility classes. For example, in `BookmarkForm.js` and `BookmarkList.js`, you’ll see class names already tailored to Tailwind. If you choose a different styling approach, ensure you apply appropriate styles to make the application visually appealing and user-friendly. For instance, you could use a CSS framework like Bootstrap or Material UI, or write your own custom CSS.

Testing Your Bookmarker App

Now, start your Next.js development server if it’s not already running (`npm run dev`). Open your browser and navigate to `http://localhost:3000`. You should see the bookmarking application. Try the following:

  1. Enter a URL in the input field and click “Add Bookmark.” The URL should appear in the list.
  2. Click the “Delete” button next to a bookmark. The bookmark should be removed from the list.
  3. Refresh the page. Your bookmarks should still be there.

If everything works as expected, congratulations! You’ve successfully built a simple, interactive bookmarking application with Next.js.

Common Mistakes and How to Fix Them

Here are some common mistakes and how to fix them:

  • Incorrect Import Paths: Double-check your import paths in components. Make sure the file names and directory structures are correct.
  • Missing `useState` or `useEffect` Imports: Ensure you’ve imported `useState` and `useEffect` from `react` if you’re using them.
  • Incorrect Prop Passing: Verify that you’re passing the correct props to the child components and that the props are being used correctly within those components.
  • Local Storage Errors: Remember that `localStorage` is only available in the browser. Use the `typeof window !== ‘undefined’` check to prevent errors during server-side rendering (SSR).
  • Not Clearing Input Field: If the input field isn’t clearing after adding a bookmark, make sure you’re setting the `url` state back to an empty string in the `handleSubmit` function of the `BookmarkForm` component.
  • Deleting the Wrong Bookmark: Ensure your `handleDeleteBookmark` function correctly identifies the bookmark to delete based on the index.

Key Takeaways

This tutorial has provided a practical, step-by-step guide to building a simple bookmarking application using Next.js. You’ve learned how to:

  • Set up a Next.js project.
  • Create and use React components (form and list).
  • Manage component state using `useState`.
  • Use `useEffect` to handle side effects (loading and saving bookmarks to local storage).
  • Pass data between components using props.
  • Implement basic styling.

FAQ

Here are some frequently asked questions:

  1. Can I deploy this application? Yes, you can deploy your Next.js application to platforms like Vercel, Netlify, or AWS. You’ll need to build the application for production using `npm run build` before deploying.
  2. How can I add more features? You can extend this application by adding features such as tagging, categories, search functionality, import/export options, and user authentication.
  3. Can I use a database instead of local storage? Yes, for more robust bookmark management, you can integrate a database (e.g., MongoDB, PostgreSQL) to store your bookmarks. This would require setting up a backend API to interact with the database.
  4. How do I handle errors? Implement error handling within your components to gracefully manage potential issues, such as invalid URLs or local storage errors. Display user-friendly error messages.
  5. Is this application responsive? While this basic implementation doesn’t explicitly focus on responsiveness, you should add responsive design principles to ensure your app looks good on various devices. This typically involves using media queries in your CSS or leveraging a responsive CSS framework.

This project serves as a foundation for building more complex web applications with Next.js. You’ve gained hands-on experience with key concepts, and the knowledge you’ve acquired can be readily applied to other projects. Remember to experiment with different features, explore advanced styling techniques, and most importantly, keep learning. Building this bookmarking app is just the beginning; the possibilities are vast. This project, while simple, demonstrates a functional and useful application of Next.js, and provides a starting point for more complex applications. Now go forth and bookmark!