Build a Simple Next.js Interactive Web-Based Link Shortener

Written by

in

In today’s fast-paced digital world, sharing long, unwieldy URLs is a common frustration. Whether you’re tweeting a link, sharing it in an email, or simply trying to make it more readable, a lengthy URL can be off-putting. This is where a link shortener comes in handy. It transforms those complex web addresses into concise, manageable links, making sharing and tracking easier. In this tutorial, we’ll build a simple, yet functional, link shortener using Next.js, a powerful React framework, to help you understand how to create a useful application. We’ll cover everything from setting up the project to deploying it, ensuring a comprehensive learning experience for developers of all levels.

Why Build a Link Shortener?

Creating a link shortener is a great way to learn about several key web development concepts. It involves working with:

  • Frontend Development: Building user interfaces with React and Next.js.
  • Backend Development: Handling API requests and data storage (we’ll use a simple approach for this tutorial).
  • URL Handling: Understanding how URLs work and how to manipulate them.
  • Database Interactions: (Even a basic one) for storing and retrieving shortened links.

Beyond the technical skills, building a link shortener provides a practical application that you can use daily. It’s a project you can showcase in your portfolio and a valuable tool for anyone who frequently shares links. Furthermore, it’s an excellent way to grasp the fundamentals of full-stack development.

Prerequisites

Before we dive in, ensure you have the following installed:

  • Node.js and npm (or yarn): You’ll need Node.js installed to run Next.js. npm (Node Package Manager) or yarn will be used to manage project dependencies. You can download it from the official Node.js website.
  • A Code Editor: A code editor like Visual Studio Code, Sublime Text, or Atom will make the development process much easier.
  • Basic Understanding of JavaScript and React: Familiarity with JavaScript and React is beneficial, although we’ll explain the concepts as we go.

Setting Up Your Next.js Project

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

npx create-next-app link-shortener

This command sets up a new Next.js project named “link-shortener”. Navigate into the project directory:

cd link-shortener

Now, start the development server:

npm run dev

You should see a message in your terminal indicating that the server is running, typically on http://localhost:3000. Open this address in your browser, and you should see the default Next.js welcome page. This confirms that your project is set up correctly.

Project Structure Overview

Before we start coding, let’s briefly examine the basic structure of a Next.js project:

  • pages/: This is where you’ll create your pages. Each file in this directory represents a route in your application. For example, pages/index.js will be accessible at the root path (/).
  • public/: This directory is for static assets such as images, fonts, and other files.
  • styles/: This directory is for your CSS files. Next.js supports CSS Modules, styled-jsx, and other styling solutions.
  • package.json: This file contains information about your project, including dependencies and scripts.

Building the Frontend: The User Interface

Our link shortener’s frontend will be simple: a form for entering the long URL and a display for the shortened URL. We will modify the pages/index.js file.

Replace the content of pages/index.js with the following code:

import { useState } from 'react';

export default function Home() {
  const [longURL, setLongURL] = useState('');
  const [shortURL, setShortURL] = useState('');
  const [error, setError] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    setError('');
    setShortURL('');

    if (!longURL) {
      setError('Please enter a URL.');
      return;
    }

    try {
      const response = await fetch('/api/shorten', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ longURL }),
      });

      const data = await response.json();

      if (response.ok) {
        setShortURL(data.shortURL);
      } else {
        setError(data.error || 'Something went wrong.');
      }
    } catch (err) {
      setError('An unexpected error occurred.');
    }
  };

  return (
    <div className="container">
      <h1>URL Shortener</h1>
      <form onSubmit={handleSubmit}>
        <label htmlFor="longURL">Enter URL:</label>
        <input
          type="url"
          id="longURL"
          value={longURL}
          onChange={(e) => setLongURL(e.target.value)}
          required
        />
        <button type="submit">Shorten</button>
        {error && <p className="error">{error}</p>}
      </form>
      {shortURL && (
        <div className="result">
          <p>Shortened URL: <a href={shortURL} target="_blank" rel="noopener noreferrer">{shortURL}</a></p>
        </div>
      )}
    </div>
  );
}

Let’s break down this code:

  • Importing useState: We import the useState hook from React to manage the state of our component (the long URL, the shortened URL, and any potential errors).
  • State Variables: We declare three state variables: longURL (to store the long URL entered by the user), shortURL (to store the shortened URL received from the API), and error (to display any error messages).
  • handleSubmit Function: This function is triggered when the form is submitted. It prevents the default form submission behavior, validates the input, and sends a POST request to our API endpoint (which we will create later).
  • API Request: Inside handleSubmit, we use the fetch API to send a POST request to /api/shorten (our API route) with the long URL in the request body.
  • Handling the Response: After receiving the response from the API, we parse the JSON data. If the response is successful (status code 200-299), we update the shortURL state with the shortened URL. If there’s an error, we update the error state.
  • JSX Structure: The JSX (JavaScript XML) part of the code defines the structure of our UI. It includes a form with an input field for the long URL, a submit button, and a display area for the shortened URL and error messages.

Now, let’s add some basic styling to the component. Create a styles/Home.module.css file with the following content:

.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px;
  font-family: sans-serif;
}

form {
  display: flex;
  flex-direction: column;
  margin-bottom: 20px;
}

label {
  margin-bottom: 5px;
}

input {
  padding: 8px;
  margin-bottom: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
}

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

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

.error {
  color: red;
  margin-top: 10px;
}

.result {
  margin-top: 20px;
  text-align: center;
}

Import this CSS module into your pages/index.js file:

import styles from '../styles/Home.module.css';

And apply the styles to your components:

<div className={styles.container}>
  <h1>URL Shortener</h1>
  <form onSubmit={handleSubmit} className={styles.form}>
    <label htmlFor="longURL">Enter URL:</label>
    <input
      type="url"
      id="longURL"
      value={longURL}
      onChange={(e) => setLongURL(e.target.value)}
      required
    />
    <button type="submit">Shorten</button>
    {error && <p className={styles.error}>{error}</p>}
  </form>
  {shortURL && (
    <div className={styles.result}>
      <p>Shortened URL: <a href={shortURL} target="_blank" rel="noopener noreferrer">{shortURL}</a></p>
    </div>
  )}
</div>

Now, when you refresh your browser, you should see a basic form with an input field, a submit button, and a display area for the shortened URL. However, the functionality will not work until we create the API endpoint.

Building the Backend: The API Route

Next.js makes it easy to create API routes. These routes handle the backend logic for our application. We’ll create an API endpoint to receive the long URL, generate a short URL, and return it to the frontend.

Create a new file in the pages/api directory named shorten.js. This file will contain the logic for our API endpoint. Add the following code:

import { nanoid } from 'nanoid';

const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'; // Default to localhost

// In-memory storage (replace with a database in a real application)
let links = [];

export default async function handler(req, res) {
  if (req.method === 'POST') {
    const { longURL } = req.body;

    if (!longURL) {
      return res.status(400).json({ error: 'URL is required' });
    }

    try {
      const shortCode = nanoid(6);
      const shortURL = `${BASE_URL}/${shortCode}`;

      // Simple data storage (in-memory for this example)
      links.push({ shortCode, longURL });

      res.status(200).json({ shortURL });
    } catch (error) {
      console.error(error);
      res.status(500).json({ error: 'Failed to shorten URL' });
    }
  }
  // Handle GET requests (for redirection)
  else if (req.method === 'GET') {
    const { shortCode } = req.query;

    if (!shortCode) {
      return res.status(400).json({ error: 'Short code is required' });
    }

    const link = links.find((link) => link.shortCode === shortCode);

    if (link) {
      res.redirect(301, link.longURL);
    } else {
      res.status(404).json({ error: 'Link not found' });
    }
  }
  else {
    res.status(405).json({ error: 'Method not allowed' });
  }
}

Let’s break down this code:

  • Importing nanoid: We import the nanoid function from the `nanoid` library. This library is used to generate unique, short, and URL-friendly IDs. Install it using: npm install nanoid
  • BASE_URL: We define a BASE_URL variable. This is important for generating the short URL. It defaults to http://localhost:3000 but will ideally be set using an environment variable in production. This allows us to make the app work on a deployed server without hardcoding the domain.
  • In-memory storage (links): For simplicity, we’re using an in-memory array called links to store the long and short URLs. Important: In a real-world application, you would replace this with a database (e.g., MongoDB, PostgreSQL, or a service like Firebase).
  • handler Function: This is the main function that handles incoming requests to the API endpoint. It’s an asynchronous function (async) because we might perform operations that take time, like database calls.
  • POST Request Handling: If the request method is POST (which our frontend uses), we extract the longURL from the request body.
  • Input Validation: We check if the longURL is provided. If not, we return an error.
  • Generating the Short Code: We use nanoid(6) to generate a random 6-character short code. The length (6) can be adjusted.
  • Creating the Short URL: We construct the full short URL by combining the BASE_URL with the short code.
  • Storing the Link: We store the longURL and shortCode in our links array. In a real app, this would be a database operation.
  • Returning the Response: We send a JSON response back to the client with the shortURL.
  • GET Request Handling (Redirection): The API endpoint also handles GET requests. This is for redirecting users when they click on the shortened URL.
  • Extracting the Short Code: We extract the shortCode from the query parameters (e.g., /api/shorten?shortCode=xyz123).
  • Finding the Link: We search for the corresponding longURL in our links array.
  • Redirecting the User: If the link is found, we use res.redirect(301, link.longURL) to redirect the user to the original URL. The 301 status code indicates a permanent redirect.
  • Error Handling: If the link is not found, we return a 404 error.
  • Method Not Allowed: If the request method is not POST or GET, we return a 405 error (Method Not Allowed).

Before you test, you will need to install the `nanoid` package by running this command in your terminal:

npm install nanoid

After installing nanoid, make sure you have restarted your development server (npm run dev) for the changes to take effect.

Testing Your Link Shortener

Now, let’s test our application.

  1. Open your Next.js application in your browser (usually at http://localhost:3000).
  2. Enter a long URL into the input field (e.g., https://www.example.com).
  3. Click the “Shorten” button.
  4. If everything works correctly, you should see a shortened URL displayed. It will look something like http://localhost:3000/someRandomCode.
  5. Click on the shortened URL. You should be redirected to the original long URL (e.g., https://www.example.com).

If you encounter any errors, check the browser’s developer console for error messages. Also, double-check your code and ensure you have followed all the steps.

Deployment

Once you’ve tested your link shortener locally and are satisfied with its functionality, you’ll likely want to deploy it so others can use it. Next.js applications are easy to deploy. Here’s a general overview of the deployment process. The specifics will depend on the platform you choose.

Popular Deployment Platforms:

  • Vercel: Vercel is the company behind Next.js, and it provides a seamless deployment experience. It’s often the easiest and fastest way to deploy a Next.js app. Deploying to Vercel is extremely simple. You can often deploy directly from your Git repository (e.g., GitHub, GitLab). Vercel automatically handles building and deploying your application.
  • Netlify: Netlify is another popular platform for deploying web applications. It offers similar features to Vercel, including automatic builds, deployments, and CDN (Content Delivery Network) capabilities.
  • Other Platforms: You can also deploy Next.js apps to other platforms like AWS (Amazon Web Services), Google Cloud Platform, or any platform that supports Node.js applications. These options often require more manual configuration.

Steps for Deployment (General):

  1. Choose a Platform: Select a deployment platform (e.g., Vercel, Netlify).
  2. Connect Your Repository: Connect your Git repository (where your code is stored) to the platform.
  3. Configure Build Settings: The platform will usually detect that it’s a Next.js project and automatically set up the build settings. You may need to specify the build command (usually npm run build or yarn build) and the output directory (usually .next).
  4. Deploy: Deploy your application. The platform will build your application and deploy it to its servers.
  5. Configure Environment Variables: You’ll likely need to configure environment variables for your application. For example, the NEXT_PUBLIC_BASE_URL variable. This is crucial to the app working on a deployed server.
  6. Verify and Test: After deployment, verify that your application is working correctly by testing the shortened URLs.

Important Considerations for Production:

  • Environment Variables: Use environment variables to store sensitive information (e.g., database credentials, API keys) and configuration settings. This ensures that your secrets are not hardcoded in your codebase and are not exposed.
  • Database: Replace the in-memory links array with a persistent database. Choose a database that suits your needs (e.g., MongoDB, PostgreSQL, or a service like Firebase).
  • Error Handling and Logging: Implement robust error handling and logging to monitor your application’s performance and identify any issues.
  • Rate Limiting: Implement rate limiting to prevent abuse and ensure fair usage of your link shortener.
  • URL Validation: Validate the long URLs entered by the user to prevent malicious input and ensure the URLs are valid.
  • Security: Implement security best practices to protect your application from vulnerabilities (e.g., cross-site scripting (XSS) attacks, SQL injection).
  • Analytics: Consider adding analytics to track the performance of your shortened links (e.g., clicks, referrers).

Common Mistakes and Troubleshooting

Here are some common mistakes and how to fix them:

  • CORS Errors: If you’re getting CORS (Cross-Origin Resource Sharing) errors, it means your frontend is trying to access your API from a different domain than where the API is hosted. This is a common issue when developing locally and deploying to a different domain. The easiest solution for local development is to set up a proxy. In Next.js, you can configure a proxy in your next.config.js file. For production, ensure your API and frontend are hosted on the same domain or configure CORS headers on your API.
  • Incorrect BASE_URL: If your shortened URLs are not working, double-check that your BASE_URL environment variable is set correctly. If you’re deploying to Vercel or Netlify, they usually provide a way to set environment variables in their dashboard. If the BASE_URL is incorrect, the shortened links will point to the wrong location.
  • Missing Dependencies: Make sure you have installed all the necessary dependencies (e.g., nanoid) using npm or yarn.
  • API Route Errors: If your API route is not working, check the browser’s developer console for any errors. Also, verify that your API route file (pages/api/shorten.js) is correctly placed and that the code is free of syntax errors.
  • Incorrect Form Submission: Ensure that your form’s onSubmit handler is correctly implemented and that the handleSubmit function is preventing the default form submission behavior (e.preventDefault()).
  • Database Connection Errors: If you’re using a database, check your database connection details (e.g., host, username, password) and ensure that your application can connect to the database.

Key Takeaways

This tutorial has walked you through building a simple link shortener using Next.js. You’ve learned how to set up a Next.js project, create a frontend with React components, build an API route, and handle form submissions. You’ve also learned about deployment and common issues.

Here’s a summary of the key takeaways:

  • Next.js is a powerful framework: Next.js simplifies building web applications with its features like server-side rendering, static site generation, and API routes.
  • API routes are easy to create: Next.js makes it simple to create API endpoints in your application.
  • Use environment variables: Always use environment variables for sensitive information and configuration settings.
  • Choose a database: Replace in-memory storage with a database in a real-world application.
  • Deployment is straightforward: Platforms like Vercel and Netlify make deploying Next.js applications easy.

FAQ

Here are some frequently asked questions about building a link shortener:

  1. How can I make the shortened URLs more memorable?
    • You can customize the short code generation. Instead of using nanoid(6), you could allow the user to choose a custom alias (but be aware of potential conflicts and security implications).
    • Implement a database to store and retrieve the links.
  2. What database should I use?
    • The best database depends on your needs. For a simple link shortener, you could use a NoSQL database like MongoDB or a relational database like PostgreSQL. Firebase is also a good option if you want a managed database service.
  3. How can I track the number of clicks on a shortened URL?
    • You’ll need to store the click count in your database. When a user clicks on a shortened URL, you can increment the click count in the database.
    • You could also add analytics to track the performance of your shortened links (e.g., clicks, referrers).
  4. How can I prevent abuse of my link shortener?
    • Implement rate limiting to prevent users from creating too many shortened URLs in a short period.
    • Validate the long URLs to ensure they are valid and don’t contain any malicious code.

Building a link shortener is a fantastic learning experience that combines frontend and backend development. While the example provided here is basic, it serves as a solid foundation for more complex features and a deeper understanding of web application development. The skills you gain from this project can be applied to many other types of web applications. You can extend this project by adding features like analytics, custom short codes, and user accounts. The possibilities are endless. Keep experimenting, keep learning, and you’ll be well on your way to becoming a proficient web developer. The journey of learning to code is always ongoing, with new technologies and techniques constantly emerging, but projects like this provide a practical and engaging way to stay current and improve your skills. Embrace the challenge, and enjoy the process of creating something useful from scratch.