How to Build a Link-shortening App with React and Bitly API.
Tags: webdev, react, javascript, programming
Published: 2023-03-19 01:15:21

Link shortening is a way to reduce an excessively long URL into a short URL using link management software.
 
In this tutorial, I teach how to make a link shortener using the Bitly API and React.

Here's a demo of the app.

![demo of link shortening app](https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExNTFlY2QyZWYzYzk4MDUzN2YzNDQ4ZWJmMTliZjI5MDkyMDIxZDU5NCZjdD1n/lKoMR31dkWl2wKJodE/giphy.gif)
	
> Bitly is a URL-shortening service and a link management platform.
-Bitly

React is a javascript front-end library for making user interfaces.

**Note:** This tutorial uses node v18, and react v18.

let's dive in.

## Bootstrapping a new react app.

Run the code below in your terminal to start a new react project:
```
npx create-react-app app_name
```
The command automatically creates a folder with the name specified for the project.

After react app is set up successfully, navigate into your react project directory by running: 
```
cd app_name
```

In your project directory, install React-copy-to-clipboard by running the following command in your terminal:
 ```
npm install react-copy-to-clipboard
```

Now, run the following command to view your application on **localhost:3000**.

```
npm run start
```

## Restructuring project directory
 
Open your text editor and structure your **./src** directory to look like so,

```
|--/src
|  |--/icons
|  |--Home.css
|  |--Home.js
|  |--index.css
|  |--index.js
```


You may delete `App.test.js`, `setupTest.js`, and `reportWebVitals.js` from your directory as you'll not need them in this tutorial.

**icons** folder will contain icons you'll use in the app.

## Making the UI component.

Add the following code to your `/src/Home.js` file: 
```js
import { useState } from "react";
import { CopyToClipboard } from "react-copy-to-clipboard";
import "./Home.css";
import send from './icons/send.png';
import copyicon from './icons/copy.png';
import copiedicon from './icons/copied.png';

function Home() {
  const [longURL, setLongUrl] = useState("");
  const [shortLink, setShortLink] = useState({});
  const [active, setActive] = useState(false);
  const [copy, setCopy] = useState(false);

  function handleChange(e) {
    setLongUrl(e.target.value);
  }

  async function handleSubmit(e) {
    e.preventDefault();
    await fetch("https://api-ssl.bitly.com/v4/shorten", {
      method: "POST",
      mode: "cors",
      headers: {
        Authorization: `Bearer ${process.env.REACT_APP_BITLY_TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        long_url: longURL,
        domain: "bit.ly",
        group_guid: `${process.env.REACT_APP_GUID}`,
      }),
    })
      .then((res) => res.json())
      .then((data) => {
        const new_link = data.link.replace("https://", "");
        fetch(
          `https://api-ssl.bitly.com/v4/bitlinks/${new_link}/qr?image_format=png`,
          {
            mode: "cors",
            headers: {
              Authorization: `Bearer ${process.env.REACT_APP_BITLY_TOKEN}`,
            },
          }
        )
          .then((response) => response.json())
          .then((result) => {
            setShortLink(result);
            setActive(true);
          });
      });
    setLongUrl("");
  }

  return (
    <div className="App">
      <h2>A Simple Bitly Link Shortener</h2>
      <div>
        <form method="post" action="" onSubmit={handleSubmit}>
          <input
            name="long_url"
            type="text"
            value={longURL}
            placeholder="Paste your url"
            onChange={handleChange}
          />
          <button type="submit"><img src={send} alt="send icon" id="send_icon"/></button>
        </form>
        
      </div>

      {/* show on success... */}

      {active ? (
        <div className="show_links">
          <img src={shortLink.qr_code} alt="Qr code" className="qr_img"/>
          <div>
            <h3>Here's your short link...</h3>
            <span>
              <p>{shortLink.link}</p>
              <CopyToClipboard onCopy={()=>{
                setCopy(true);
              }} text={shortLink.link}>{ !copy ? <img src={copyicon} alt="copy icon" width="17px" height="17px"/> : <img src={copiedicon} alt="copy icon" width="17px" height="17px"/>  }</CopyToClipboard>

            </span>
          </div>
        </div>
      ) : (
        ""
      )}
    </div>
  );
}

export default Home;

```

The **`async handleSubmit function`** is called when we click the submit button. You'll notice that it contains two fetch methods, one contained in the other. The first fetch method submits the URL to bitly and passes the bit.ly link received in the response object to the second fetch method which is then used to request the QR code.

The **`handleChange function`** is called to update the input field as we type.

Now,  go to `/src/index.js` and override its content with the following code.

```js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import Home from "./Home";
import reportWebVitals from "./reportWebVitals";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <Home />
  </React.StrictMode>
);

reportWebVitals(console.log))
reportWebVitals();

```

The code blocks above imports and renders the home component in the root node.

### Adding project icons.

[Download](http://bit.ly/42kos3x) the icons from Mediafire, unzip and paste its content in `/src/icons`.

### Styling the component.

Note: In this tutorial, we'll not focus on CSS so I prepared some CSS to add to the project.

In your `/src/Home.css` file add the following code:

```css
* {
  margin:0;
}
.App {
width:100%;
height:100vh! important;
text-align:center;
background-color:rgb(207, 207, 207);
display:flex;
align-items:center;
justify-content:center;
flex-direction:column;
}

h2{
  padding:0 0 20px;
  font-size:2rem;
}

input[type=text] {
  font-size: 1.2rem;
  width: 300px;
  height: 100%;
  border-radius: 8px 0 0 8px;
 background-color: rgb(233, 233, 233);
 text-align: center;
 padding: 10px 20px;
 border: none;
}
input[type=text]:focus {
  outline: none;
  border: none;
}
button{
  font-size:1.2rem;
  box-sizing: border-box;
  height:100%;
  padding:10px 25px;
  border:none;
  border-radius:0 8px 8px 0;
}
button:hover{
  background-color: rgb(39, 137, 179);
}
#send_icon{
  width:21px; 
  height:1.2rem;
  vertical-align: middle;
}
.show_links {
  padding: 10px;
  margin-top: 20px;
  display: flex;
  flex-direction: row;
  column-gap: 20px;
  align-items: flex-end;
  background-color: rgb(233, 233, 233);
  width:400px;
  height:100px;
  animation-name: fadeIn;
  animation-duration: 3s;
}
@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.qr_img {
  width: 100px;
  height: inherit;
  background-color: black;
  color: white;
}
.show_links  p {
  width: 200px;
  padding: 10px 15px;
  text-align: left;
  font-weight: 500;
  border: 1.5px solid rgb(179, 179, 179);
  border-radius: 8px;
}
 span {
  display: flex;
  flex-direction:row;
  padding: 10px 0;
  column-gap: 15px;
  justify-content: center;
  align-items: center;
}
h3 {
  text-align: left;
  margin-bottom: 10px;
  color: rgb(39, 137, 179);
}

/*for smaller devices */
@media (max-width:700px){
  h2{
    padding:0 0 20px;
    font-size:1.3rem;
  }
  .show_links {
    padding: 15px;
    margin-top: 20px;
    display: block;
    width:200px;
    height:300px;
  }
  .qr_img {
    width: 200px;
    height: 200px;
    margin: auto;
  }
  h3 {
    margin: 10px 0;
    color: rgb(39, 137, 179);
    font-size: 1.05rem;
  }
  input[type=text] {
    font-size: 1rem;
    width: 170px;
    height: 100%;
    border-radius: 8px 0 0 8px;
   background-color: rgb(233, 233, 233);
   text-align: center;
   padding: 10px 20px;
   border: none;
  }
  button {
    font-size: 1rem;
  }
  #send_icon {
    width: 1.05rem;
    height: 1rem;
    vertical-align: middle;
  }
  .show_links  p {
    width: 170px;
    font-size:0.9rem;
  }
  span {
    column-gap: 10px;
  }
}

```

Override your `./src/index.css` file with the following code:

```css
body {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  background-color: rgb(233, 233, 233);
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}

```

### Setting up authentication token.

Now, go to [Bitly.com](https://bitly.com/) and create an account if you don't already have one.

Login into your dashboard and navigate to **Settings >> API**

![Bitly settings dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qnk25emfzchyb7j9a9z6.png)

Enter your Password and generate your API token.

In your root directory, create a `.env` file and add the following code:

```
REACT_APP_BITLY_TOKEN = 'enter authorization token here'
REACT_APP_GUID = 'enter guid here'

```

Guid can be copied from your Bitly account URL `https://app.bitly.com/[guid_here]/bitlinks`.

Now, add **.env** to your **.gitignore** file.


## Pushing Repo to GitHub.

- Run `git init` in your projects directory to turn our local environment into a repository.

- Now, go on and log in to your GitHub account and make a new repository.

- Back in your terminal let's connect the local repository to GitHub, run `git remote add origin <url to your GitHub repository>`.

- Change your branch to match the branch on GitHub by running `git branch -M main`.

- Run `git add --all`, to stage your files.

- Run `git commit -m "your_commit_message"` to commit your files.

- Finally, run `git push -u origin main` to push to GitHub.

Now, refresh your GitHub repo to reflect changes.

## Deploying application on Netlify.

- Go to [netlify.com](https://www.netlify.com/) and sign up for an account if you don't already have one.

![Netlify](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ae4b4fh94caj20o2imjo.png)

- Login and navigate to Sites from your dashboard.

- Click **"Import from GitHub"** and connect Netlify to GitHub on the page that follows.

- Next, authorize Netlify and select the repo you just created.


- Next, Click "Deploy site" to build.

- After a successful build, Netlify will automatically assign a name to your app, you can change that under **Site overview >> Site settings tab >> "Change site name"**.

![Netlify Site settings tab](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t2bpgdtx9tv0theqx3r7.png)

- Let's add our environment variables to Netlify. Copy the content of your .env file, go to **Site settings >> Environment Variable >> "Add a variable"** and select **"Import from a .env file"**, paste your content and click **"Import variable"**.

![Netlify Site settings tab](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sumwv981i3goi29ugnd8.png)

- Go to the Deploys tab, click **"Trigger deploy"** and redeploy to use the environment variables in your live app.

![Netlify deploys tab](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ixdv11cg4xeb1br7wl4.png)

## Conclusion.
That's it! If you've made it this far without an error then you have successfully built your link-shortening app. Hurray! :raised_hands:. I encourage you to deploy the application and share it with your friends, you can also share it with me on Twitter @amotabil8.

P.S. If you liked the project do react to my post and if you ran into an error do share a comment and I'll do my best to help you resolve it. :v:

**Resources.**

Here's a link to the [GitHub repository](https://github.com/amissah17/linkshortener).

Learn more about [React](https://beta.reactjs.org/).

Learn more about [Bitly's API](https://dev.bitly.com/).

Learn more about [Netlify](https://docs.netlify.com/).