Build a Social Media Link Preview App with Cloudinary and Auth0
Tags: hackmamba, jamstack, auth0, cloudinary
Published: 2021-10-20 12:38:00

---
title: Build a Social Media Link Preview App with Cloudinary and Auth0
published: true
description: Learn how to use Auth0 and Cloudinary to build an Open Graph Tag Generator Web App
tags: hackmamba,jamstack,auth0,cloudinary
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t60xzs9nh5v7rjqj0n1h.png
---

## **Introduction**

Authentication and File Storage are common features that developers think about while working on a project. [Cloudinary](https://cloudinary.com/) is a service that makes media management easy to integrate for web and mobile developers. [Auth0](https://auth0.com/) provides authentication and authorization services that are built into applications.
In this tutorial, we are going to build a real-world application that integrates with Auth0 and Cloudinary.

## **What We'll Be Building**

One of the major reasons for creating content on the internet is to share it with others which often would be via social media. When sharing that URL link depending on the site shared it would most likely use rich previews to show more information about the site in a card view. The [Open Graph](https://ogp.me/) Protocol was developed to standardize the display of link previews and other metadata within a webpage. You can read more about it in Colby's article [here](https://www.freecodecamp.org/news/what-is-open-graph-and-how-can-i-use-it-for-my-website/#why-do-i-need-it).
In this tutorial, we will build a Site Previewer App that displays link previews for social media sites and displays the page metadata.




![Application Demo](https://paper-attachments.dropbox.com/s_E5CB7A7C8BE614D1F5C7C8CABC484636FE8FE2184E12168C9E9F3950E3B111A1_1632683067462_screenshot.png)


You can find the Source Code of the project on [Github](https://github.com/DavidAmunga/site-preview-app-tutorial) and Live Environment Setup on [CodeSandbox](https://codesandbox.io/s/auth0-cloudinary-react-m2y1q)

## Prerequisites
- Basic understanding of Javascript
- Basic knowledge of React and React Hooks
- The Latest version of Nodejs installed on your machine
- the Latest version of React
- A Cloudinary account. *Creating an Account is free*
- An Auth0 Account. *Creating an Account is free*

## Set up the Project
- We will be using [TailwindCSS](https://tailwindcss.com) as our CSS Framework. Setup a New [Create React App](https://reactjs.org/docs/create-a-new-react-app.html) Project with TailwindCSS using this [guide](https://tailwindcss.com/docs/guides/create-react-app) from the official Tailwind Docs.


- Once you've completed setting up Tailwind from the guide above. Replace your `App.js` code to below to test your setup.

```js    
    //App.js
    function App() {
      return (
        <div>
          <h1 className="text-9xl font-bold text-yellow-400">Hello World!</h1>
        </div>
      );
    }
    
    export default App;
```

You should be getting this screen once you load `http://localhost:3000`.

![Tailwind Setup](https://paper-attachments.dropbox.com/s_E5CB7A7C8BE614D1F5C7C8CABC484636FE8FE2184E12168C9E9F3950E3B111A1_1632939505853_NUTUGi5w.jpg)



- Install `@tailwindcss/forms` library and add it to your tailwind.config.js file onto your plugins array. This will be helpful in styling form elements through TailwindCSS.

```bash
    npm install @tailwindcss/forms
```

```js
    //tailwind.config.js
    module.exports = {
     ...
      plugins: [require('@tailwindcss/forms')],
    };
```

## Step 2. Setup Authentication with Auth0 and Views
- Sign Up/Login to Auth0. You can sign up [here](https://auth0.com/signup?&signUpData=%7B%22category%22%3A%22button%22%7D). Once Logged in, create an Application then navigate on Auth0 to your application section then copy your `Domain` and `Client ID` information.
    
![Auth0 App](https://res.cloudinary.com/davidamunga-com/image/upload/v1632840631/dropbox/Screenshot_2021-09-25_at_9.21.53_PM_yepmif.png)



- In your project create an .env.local file with the following values. Insert your Copied Client ID & Domain in this file.
```    
    # YOUR_AUTH_DOMAIN is e.g yourauthdomain.eu.auth0.com
    REACT_APP_AUTH_CLIENT_ID=YOUR_CLIENT_ID
    REACT_APP_AUTH_DOMAIN=YOUR_AUTH_DOMAIN 
```

- Install the `@auth0/auth0-react` npm package from Auth0 for React.
```bash    
    npm install @auth0/auth0-react
```

- In order to manage Authentication State within the Application the Auth0 React SDK uses [React Context](https://reactjs.org/docs/context.html). We can configure the `Auth0Provider` Component from the root component in `index.js` .

```js    
    //index.js
    
    import { Auth0Provider } from "@auth0/auth0-react";
    
    ....
    ReactDOM.render(
      <Auth0Provider
        domain={process.env.REACT_APP_AUTH_DOMAIN}
        clientId={process.env.REACT_APP_AUTH_CLIENT_ID}
        redirectUri={window.location.origin}
      >
        <App />
      </Auth0Provider>,
      document.getElementById("root")
    );
```
The `redirectUri` is set to redirect to `window.location.origin`


- Update your `App.js` to have a basic authentication setup.

```js    
    //App.js
    import { useAuth0 } from '@auth0/auth0-react';
    import './App.css';
    
    function App() {
      const { loginWithRedirect, logout, isAuthenticated } = useAuth0();
    
      if (isAuthenticated) {
        return (
          <div>
            <h1 className="text-9xl font-bold text-yellow-400">Logged In !</h1>
            <button onClick={() => logout()}>Log Out</button>
          </div>
        );
      } else {
        return (
          <div>
            <h1 className="text-9xl font-bold text-yellow-400">Hello World!</h1>
            <button onClick={() => loginWithRedirect()}>Log In</button>
          </div>
        );
      }
    }
    
    export default App;
```

We can see we are able to pull Authentication State and Login/Logout actions from the `useAuth0` hook. We use `isAuthenticated` to switch between showing Content that is Authenticated and that which isn't. Once you update this you should see the Authentication state changes once we log in and out.

![Authentication Setup](https://paper-attachments.dropbox.com/s_E5CB7A7C8BE614D1F5C7C8CABC484636FE8FE2184E12168C9E9F3950E3B111A1_1632681861783_tutorial_site_preview_gif.gif)



## Step 3. Set up Login and Home UI
- Add the UI below returned from when `isAuthenticated` is false.

```js    
    // App.js
    if(isAuthenticated){
    ....
    }else{
    return(
    <div className="">
            <main className="mx-auto max-w-5xl px-4 sm:mt-12 sm:px-6 md:mt-16 lg:mt-20 lg:px-8 xl:mt-28">
              <div className="flex flex-col items-center space-y-4 text-center">
                <div className="bg-yellow-300 p-4 rounded-lg shadow-md">
                  <svg
                    class="text-yellow-700 w-20 h-20"
                    fill="none"
                    stroke="currentColor"
                    viewBox="0 0 24 24"
                    xmlns="http://www.w3.org/2000/svg"
                  >
                    <path
                      stroke-linecap="round"
                      stroke-linejoin="round"
                      stroke-width="2"
                      d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
                    ></path>
                  </svg>
                </div>
    
                <h1 className="text-4xl tracking-tight font-extrabold text-gray-800 sm:text-5xl md:text-6xl">
                  <span className="block xl:inline">Site Preview</span>{' '}
                </h1>
                <p className="mt-3 text-base text-gray-500 sm:mt-5 sm:text-lg sm:max-w-xl sm:mx-auto md:mt-5 md:text-xl lg:mx-0">
                  Preview and Generate Open Graph meta tags in seconds
                </p>
                <div className="mt-5 sm:mt-8 sm:flex sm:justify-center lg:justify-start">
                  <div className="rounded-md shadow">
                    <button
                      onClick={() => loginWithRedirect()}
                      className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-yellow-600 hover:bg-yellow-700 md:py-4 md:text-lg md:px-10"
                    >
                      Log In
                    </button>
                  </div>
                </div>
              </div>
            </main>
          </div>
    );
    }

```
- Create a new file `Home.js` in the directory `src`. Setup the Home Component as below.
    
{% gist https://gist.github.com/DavidAmunga/34f8f63729546bf48130c2509d529868 %}



We are also able to fetch the `user` object from the `useAuth0` Hook. We are able to extract the user name and display it once logged in. Your UI should appear like below once your run. We will be defining 3 fields for the Preview Tags which include : -


- Title (Should be less than 60 characters)
- Description(Should be less than 120 characters).
- Image (URL string)
    
![Home UI](https://paper-attachments.dropbox.com/s_E5CB7A7C8BE614D1F5C7C8CABC484636FE8FE2184E12168C9E9F3950E3B111A1_1632681959591_Screenshot+2021-09-25+at+10.21.26+PM.png)


Let us now work on the `PreviewImage` and `CodeBlock` component.

**Setting up PreviewImage Component**
For the preview image component we will create a folder `components` inside `src` directory.

- Add Custom Styling for the Preview Image in `App.css`.
```css    
    /* App.css */
    .preview {
      font-size: 12px;
      font-family: tahoma;
      border: 1px solid #ccc;
      color: #555;
      box-sizing: border-box;
      text-decoration: none;
    }
```


- Create the Component

```js   
    // src/components/PreviewImage.js
    
    import React from 'react';
    
    const PreviewImage = ({ form }) => {
      return (
        <div class="preview flex flex-col">
          <img
            className="w-full object-cover h-32"
            src={form.image}
            alt="some description"
          />
          <div className="px-2 py-2 bg-white">
            <h3 className="text-gray-800 font-bold">
              {form.title.length === 0 ? `Enter A Title` : form.title}
            </h3>
            <p class="">
              {form.description.length === 0
                ? `Enter A Description`
                : `${form.description}`}
            </p>
          </div>
        </div>
      );
    };
    
    export default PreviewImage;
```

- Import the Component to `Home.js`

```js    
    //Home.js
    import PreviewImage from './components/PreviewImage';
    
    const Home = ()=>{
    ...
    return (
    ...
    <div className="grid grid-cols-2 gap-6">
        <form>...
        <div>
                {/* Add Preview Image Component Here */}
                <PreviewImage form={form} />
        <div>
    </div>
    )
    }
```

**Set up CodeBlock Component with Syntax Highlighting**
The Last Part of the Home UI setup involves adding a CodeBlock section which displays the tags displayed by completing the form fields entered on the same page. We will display the Metadata Code using Prism which is an elegant syntax highlighter tool.


- Install `prismjs` package

```bash    
    npm install prismjs
```

- Download you syntax highlighter css from [here](https://prismjs.com/download.html#themes=prism-okaidia&languages=markup+css+clike+javascript). For this example we are going to use `Okaidia` .Remember the download css option is near the bottom of the prismjs download page. Once downloaded place it at the root of the `src` directory.

```js    
    import React from 'react';
    import Prism from 'prismjs';
    import '../prism.css';
    
    const Codeblock = ({ form }) => {
      const { title, description, image } = form;
      React.useEffect(() => {
        Prism.highlightAll();
      }, [form]);
    
      return (
        <div className="flex flex-col mt-4">
          <div className="flex justify-between items-center bg-gray-200 p-4 rounded-sm">
            <h3 className="text-md text-gray-800 font-semibold">Code</h3>
          </div>
          <pre>
            <code className="language-html">
              {`
    <!-- Replace YOUR_SITE_NAME with the Page/Website URL e.g https://example.com -->
    <!-- HTML Meta Tags -->
    <title>${title}</title>
    <meta name="description" content="${description}">
    
    <!-- Facebook Meta Tags -->
    <meta property="og:url" content="YOUR_SITE_NAME">
    <meta property="og:type" content="website">
    <meta property="og:title" content="${title}">
    <meta property="og:description" content="${description}">
    <meta property="og:image" content="${image}">
    
    <!-- Twitter Meta Tags -->
    <meta name="twitter:card" content="summary_large_image">
    <meta property="twitter:domain" content="YOUR_SITE_NAME">
    <meta property="twitter:url" content="YOUR_SITE_NAME">
    <meta name="twitter:title" content="${title}">
    <meta name="twitter:description" content="${description}">
    <meta name="twitter:image" content="${image}">
      `}
            </code>
          </pre>
        </div>
      );
    };
    
    export default Codeblock;
```

- Import the Codeblock Component to `Home.js`
 
```js  
    //Home.js
    import Codeblock from './components/Codeblock';
    
    const Home = ()=>{
    ...
    return (
    ...
    <div className="grid grid-cols-2 gap-6">
    ...
    </div>
    {/* Add Code Block Component Here */}
    <Codeblock form={form} />
    )
    }
```

Once you are done you should have both the PreviewImage and Codeblock rendered properly as below.


![Home UI Complete](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ey09sai81wwuw5ytysjn.png)

## Step 4. Setup Image Uploads with Cloudinary

The application is nearly complete. All we are missing now is adding an Image Upload feature so that it can populate the final image url to the Codeblock and PreviewImage component.

Next head over to **Cloudinary** and create an account or login. Once logged in you can check from your Dashboard to see a summary of Account Details,Resources. Copy the _Cloud name_ value you see in the Account Details.


    
![Cloudinary Dashboard](https://paper-attachments.dropbox.com/s_E5CB7A7C8BE614D1F5C7C8CABC484636FE8FE2184E12168C9E9F3950E3B111A1_1632682588706_Screenshot+2021-09-25+at+11.08.01+PM.png)

- Head over to the Settings section by clicking on your profile. Select the Upload Tab. Scroll down until you see the **Upload Presets** section and click on the `**Enable unsigned uploading**` **button** which allows Unauthenticated uploads which will work for this demo. Copy the Name you get in the Unsigned Mode.
    
![Cloudinary Settings](https://paper-attachments.dropbox.com/s_E5CB7A7C8BE614D1F5C7C8CABC484636FE8FE2184E12168C9E9F3950E3B111A1_1632682557757_Screenshot+2021-09-25+at+11.17.20+PM.png)



    
- Head over back to the project and add Cloudinary's widget script in your `index.html` file in the `public` folder.

```    
    <script src="//widget.cloudinary.com/global/all.js" type="text/javascript"></script>
```

- In your .env.local file add your Copied Cloud Name and Upload Preset value in this file.

```    
    REACT_APP_CLOUDINARY_UPLOAD_PRESET=
    REACT_APP_CLOUDINARY_CLOUD_NAME=
```

- In your `Home.js` you can add the following function in the Component together with the onClickHandler to the Upload File button that should fires the same function.

```js
  
    //Home.js
    
    ....
    const uploadWidget = () => {
        window.cloudinary.openUploadWidget(
          {
            cloud_name: process.env.REACT_APP_CLOUDINARY_CLOUD_NAME,
            upload_preset: process.env.REACT_APP_CLOUDINARY_UPLOAD_PRESET,
            tags: ['site-preview'],
            sources: ['local', 'url'],
          },
          function (error, result) {
            if (error) {
              return;
            }
            console.log('Upload Result is', result);
            setForm({ ...form, image: result[0].url });
          },
        );
      };
    
    ....
    return(
    ...
    <label className="block text-sm font-medium text-gray-700">
        Image
    </label>
    <button
       type="button"
       onClick={() => uploadWidget()}
         className="...">
    </button>
    )
```

This function opens the Cloudinary Widget with the specified cloud name and upload preset. It also adds options for local file upload or url upload. Once successful we update the form with the returned image from the result.

## Conclusion

Once done the solution is completed. You now have built a powerful social media link previews generator with Authentication and File Storage. There are more features that can probably be added to the app which include

- Customising Image Feature Options through the powerful Cloudinary [Transformation API](https://cloudinary.com/documentation/transformation_reference) . Might need to let the user choose the file format of image, crop, background overlays or even blur.
- View list of Images Selected depending on Auth0 Account Logged In.

Have any other ideas ? Feel free to clone the project and expand on the capabilities of the app using Cloudinary and Auth0.

_Content created for the [Hackmamba](https://content.hackmamba.io/) Jamstack Content Hackathon with Auth0 and Cloudinary_