Build a Next.js Interactive Web-Based URL Expander

Written by

in

In the digital age, we frequently encounter shortened URLs. Services like Bitly and TinyURL have become indispensable for sharing links on social media, in emails, and elsewhere. But have you ever been curious about where a shortened link actually leads? Or perhaps you’ve been tricked by a malicious shortened URL and wished you could have seen the destination beforehand? This tutorial will guide you through building a simple, yet functional, URL expander using Next.js, allowing you to reveal the full URL behind any shortened link. This project is a great way to learn about fetching data from APIs, handling user input, and building interactive web applications.

Why Build a URL Expander?

Beyond satisfying curiosity, a URL expander offers several practical benefits:

  • Security: Verify the destination of a shortened link before clicking, protecting yourself from phishing attempts and malicious websites.
  • Transparency: Understand where a link is taking you, especially when dealing with marketing campaigns or affiliate links.
  • Convenience: Quickly see the full URL without having to click the shortened link, saving time and effort.

This project is perfect for beginners and intermediate developers looking to expand their Next.js skills. We’ll be focusing on the core concepts, keeping the code clean, and providing clear explanations every step of the way.

Prerequisites

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

  • Node.js and npm (or yarn): These are essential for running and managing your Next.js project. You can download them from nodejs.org.
  • A code editor: VS Code, Sublime Text, or any editor you prefer.
  • Basic understanding of HTML, CSS, and JavaScript: Familiarity with these languages will make it easier to follow along.

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 url-expander

This command will create a new directory named `url-expander` with all the necessary files for a basic Next.js application. Navigate into the project directory:

cd url-expander

Now, start the development server:

npm run dev
# or
yarn 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.

Building the UI

Our URL expander will have a simple user interface consisting of an input field for the shortened URL, a button to expand the URL, and a display area to show the expanded URL. Let’s modify the `pages/index.js` file to create this UI.

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

import { useState } from 'react';

export default function Home() {
  const [shortUrl, setShortUrl] = useState('');
  const [expandedUrl, setExpandedUrl] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState('');

  const handleExpand = async () => {
    // Implementation will go here
  };

  return (
    <div style={{ fontFamily: 'sans-serif', padding: '20px' }}>
      <h2>URL Expander</h2>
      {error && <p style={{ color: 'red' }}>{error}</p>}
      <div style={{ marginBottom: '10px' }}>
        <label htmlFor="shortUrl">Enter Shortened URL:</label>
        <input
          type="text"
          id="shortUrl"
          value={shortUrl}
          onChange={(e) => setShortUrl(e.target.value)}
          style={{ marginLeft: '10px', padding: '5px', borderRadius: '5px', border: '1px solid #ccc' }}
        />
      </div>
      <button
        onClick={handleExpand}
        disabled={isLoading}
        style={{ padding: '10px 20px', backgroundColor: '#4CAF50', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer', opacity: isLoading ? 0.6 : 1 }}
      >
        {isLoading ? 'Expanding...' : 'Expand URL'}
      </button>
      {expandedUrl && (
        <div style={{ marginTop: '20px' }}>
          <p>Expanded URL:</p>
          <a href={expandedUrl} target="_blank" rel="noopener noreferrer">{expandedUrl}</a>
        </div>
      )}
    </div>
  );
}

Let’s break down this code:

  • Import `useState`: We import the `useState` hook from React to manage the state of our component.
  • State Variables: We define four state variables:
    • `shortUrl`: Stores the shortened URL entered by the user.
    • `expandedUrl`: Stores the expanded URL retrieved from the API.
    • `isLoading`: A boolean to indicate whether the URL is currently being expanded.
    • `error`: Stores any error messages that might occur.
  • `handleExpand` Function: This function will be triggered when the user clicks the “Expand URL” button. We’ll implement the logic to fetch the expanded URL from an API in this function later.
  • UI Elements: We have an input field for the shortened URL, a button to trigger the expansion, and a display area to show the expanded URL. We’ve also included basic styling for better readability.

After saving the file, refresh your browser. You should now see the UI for the URL expander.

Implementing the URL Expansion Logic

Now, let’s implement the `handleExpand` function to fetch the expanded URL. We’ll use a free URL expansion API. There are several options available; for this tutorial, we will use a simple API. This API doesn’t require an API key, making it easy to get started. Be aware that free APIs can sometimes have rate limits or downtime.

Add the following code inside the `handleExpand` function in `pages/index.js`:

  const handleExpand = async () => {
    setError('');
    setExpandedUrl('');
    setIsLoading(true);

    try {
      const response = await fetch(`https://api.shrtco.de/v2/expand?url=${shortUrl}`);
      const data = await response.json();

      if (data.ok) {
        setExpandedUrl(data.result.long_url);
      } else {
        setError(data.error);
      }
    } catch (err) {
      setError('An error occurred while expanding the URL.');
      console.error(err);
    } finally {
      setIsLoading(false);
    }
  };

Let’s break down this code:

  • Reset State: Before making the API call, we reset the `error` and `expandedUrl` states and set `isLoading` to `true`.
  • API Call: We use the `fetch` API to make a GET request to the URL expansion API endpoint. We construct the API URL using the `shortUrl` entered by the user.
  • Handle Response: We parse the response as JSON and check the `ok` property. If `ok` is true, we update the `expandedUrl` state with the expanded URL from the API response. If not, we set the error message.
  • Error Handling: We use a `try…catch` block to handle potential errors during the API call. If an error occurs, we set an appropriate error message. We also log the error to the console for debugging purposes.
  • Finally Block: The `finally` block ensures that `isLoading` is set to `false` regardless of whether the API call succeeds or fails. This is crucial for resetting the button state.

Testing Your URL Expander

Save the `pages/index.js` file and go back to your browser. Enter a shortened URL into the input field (e.g., a Bitly or TinyURL link) and click the “Expand URL” button. If everything works correctly, the expanded URL should appear below the button. If there’s an error, an error message will be displayed.

Here are some things to check if it’s not working:

  • Console Errors: Open your browser’s developer console (usually by pressing F12) and check for any errors. These can provide valuable clues about what’s going wrong.
  • API Errors: The API might be down or rate-limited. Try again later, or consider using a different API.
  • Network Issues: Make sure you have an active internet connection.
  • Typos: Double-check that you’ve typed the URL correctly.

Adding Input Validation (Best Practice)

To improve the user experience and prevent unexpected errors, we should add input validation to our URL expander. This will ensure that the user enters a valid URL before we make the API call. Also, we will prevent empty strings.

Modify the `handleExpand` function in `pages/index.js` as follows:

  const handleExpand = async () => {
    if (!shortUrl) {
      setError('Please enter a URL.');
      return;
    }

    // Regular expression for URL validation
    const urlRegex = /^(ftp|http|https)://(\w+.){1,}(\w{2,})(:d+)?(/S*)?$/;
    if (!urlRegex.test(shortUrl)) {
      setError('Please enter a valid URL.');
      return;
    }

    setError('');
    setExpandedUrl('');
    setIsLoading(true);

    try {
      const response = await fetch(`https://api.shrtco.de/v2/expand?url=${shortUrl}`);
      const data = await response.json();

      if (data.ok) {
        setExpandedUrl(data.result.long_url);
      } else {
        setError(data.error);
      }
    } catch (err) {
      setError('An error occurred while expanding the URL.');
      console.error(err);
    } finally {
      setIsLoading(false);
    }
  };

Here’s what we added:

  • Empty String Check: We first check if the `shortUrl` is empty. If it is, we set an error message and return.
  • URL Validation: We use a regular expression (`urlRegex`) to validate the URL format. If the URL doesn’t match the pattern, we set an error message and return.

Now, the URL expander will provide more helpful feedback to the user and prevent unnecessary API calls with invalid input.

Styling and Enhancements

While the current UI is functional, it can be improved with better styling and additional features. Here are some suggestions:

  • CSS Frameworks: Consider using a CSS framework like Tailwind CSS, Bootstrap, or Material UI to quickly style your application.
  • More Informative Error Messages: Provide more specific error messages to guide the user.
  • Copy to Clipboard: Add a button to copy the expanded URL to the clipboard.
  • API Rate Limiting: Implement logic to handle API rate limits gracefully.
  • Loading Indicators: Enhance the loading indicator (e.g., using a spinner).
  • Save History: Save the expanded URLs in the browser’s local storage to show a history.

For example, to add a simple copy-to-clipboard functionality, you can add a button next to the expanded URL and use the `navigator.clipboard.writeText()` API. You could update the return statement in `pages/index.js` to something like this:

<div style={{ marginTop: '20px', display: 'flex', alignItems: 'center' }}>
  <p style={{ marginRight: '10px' }}>Expanded URL:</p>
  <a href={expandedUrl} target="_blank" rel="noopener noreferrer" style={{ marginRight: '10px' }}>{expandedUrl}</a>
  <button
    onClick={() => {
      navigator.clipboard.writeText(expandedUrl);
      alert('Copied to clipboard!'); // Replace with a better UI
    }}
    style={{ padding: '5px 10px', backgroundColor: '#007bff', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}
  >
    Copy
  </button>
</div>

Deploying Your URL Expander

Once you’re satisfied with your URL expander, you’ll want to deploy it so others can use it. Next.js makes deployment straightforward. Here are a couple of popular options:

  • Vercel: Vercel is the platform created by the same team behind Next.js. It’s designed for easy Next.js deployments. Simply push your code to a Git repository (e.g., GitHub, GitLab, Bitbucket), and Vercel will automatically build and deploy your application.
  • Netlify: Netlify is another excellent platform for deploying static sites and web applications. It also offers automatic builds and deployments from Git repositories.

Both Vercel and Netlify offer generous free tiers, making them ideal for personal projects and learning.

Common Mistakes and Troubleshooting

Here are some common mistakes and how to fix them:

  • CORS Errors: If you encounter CORS (Cross-Origin Resource Sharing) errors, it means the API you’re trying to access doesn’t allow requests from your domain. You might need to use a proxy server or find an API that supports CORS. For the API we are using, this is less likely.
  • Incorrect API Endpoint: Double-check that you’re using the correct API endpoint and that it’s formatted correctly.
  • API Key Issues: Some APIs require an API key. Make sure you have a valid API key and that you’re including it in your API requests.
  • Typographical Errors: Typos in your code can lead to unexpected behavior. Carefully review your code for any errors.
  • State Updates: Make sure you’re updating your state variables correctly using the `set…` functions provided by the `useState` hook.
  • Network Issues: Ensure that you have a stable internet connection.

Key Takeaways

  • You’ve learned how to build a basic URL expander using Next.js.
  • You’ve gained experience with fetching data from APIs using the `fetch` API.
  • You’ve learned how to handle user input and manage application state with the `useState` hook.
  • You’ve implemented input validation to improve the user experience and prevent errors.
  • You’ve explored the basics of styling and deploying a Next.js application.

FAQ

Here are some frequently asked questions about building a URL expander:

  1. Q: Can I use a different API for URL expansion?
    A: Yes, there are many URL expansion APIs available. You can find them by searching online. Just make sure to adjust the API endpoint and data parsing logic in your code accordingly.
  2. Q: How can I handle API rate limits?
    A: If the API you’re using has rate limits, you can implement logic to handle them gracefully. This might involve displaying a message to the user, retrying the request after a delay, or using a different API.
  3. Q: How can I improve the UI of my URL expander?
    A: You can use CSS frameworks like Tailwind CSS or Bootstrap to style your application. You can also add features like a loading spinner, copy-to-clipboard functionality, and more informative error messages.
  4. Q: How can I store the expanded URLs?
    A: You can store the expanded URLs in the browser’s local storage. This will allow the user to see a history of the expanded URLs.
  5. Q: What are some security considerations?
    A: When dealing with URLs, be mindful of security. Avoid blindly clicking on expanded URLs. Consider implementing a blacklist of known malicious domains.

Building a URL expander is an excellent project for learning the fundamentals of Next.js and web development. This tutorial provides a solid foundation, and you can now expand on it by implementing more advanced features, refining the UI, and integrating with other services. Remember to practice, experiment, and don’t be afraid to try new things. The journey of learning is filled with exploration, and each project you undertake adds to your growing expertise. By embracing the process of building and iterating, you’ll not only create useful tools but also deepen your understanding of the web development landscape, paving the way for even more ambitious projects in the future.