---
# System prepended metadata

title: Lens Protocol

---

# Lens Protocol

# Overview of Lens Protocol

## The State of Social Media

We live in a time where millions of users access centralized social media applications daily. Popular sites like Twitter and Meta help us stay connected to friends, family, news updates, and more.

In this section, we'll look at the [Lens Protocol](lens.xyz) and how it can be used to build decentralized social media applications on the Polygon blockchain! We'll also explore why the Lens Protocol is a great toolkit for developers and how it can provide a more secure user experience than centralized social media.

## What is The Lens Protocol?

The **Lens Protocol** is a decentralized social graph that allows developers to build social media platforms and profiles.

What exactly is a social graph, though? A **social graph** is a collection of nodes that represent individual users, organizations, and any connections between those nodes. You can think of it as your friend lists on social media sites, your connections on LinkedIn, and who you're following on Twitter just to list a few examples.

## Decentralizing Social Media: Why Lens is Important

Web2 social media platforms like Twitter and Meta can be prone to censorship since they are centralized. A post can be removed by administration for any reason. In some countries, the government may censor the internet and some social media platforms as well.

The Lens Protocol solves this problem by not only giving developers a useful toolkit to build social media dApps, but also by giving users control over their own profiles and content.

Since the Lens Protocol is decentralized, users can create profiles and post content without the risk of their content being removed. This means that developers can use Lens to build a social network that is more secure and more connected than traditional social media platforms.

## The Lens Toolkit

What can developers do with the Lens Protocol? The protocol itself is built with modularity in mind, meaning that it has different modules (bits of code that help perform various functions) to use in the development process, making development even easier. Also, any modules added in the future will allow new features to be implemented seamlessly.

Allow me to break down the current modules and their uses:

- **Profile**: This module is what allows users to create a Lens profile. When a profile is created, a unique ProfileNFT gets minted. This ProfileNFT contains the history of the user's posts, comments, and any other content that the user has created.
- **Post**: Implementing this module allows users to post and share content. IPFS and other media types are supported.
- **Collect**: The Collect module will allow users to collect posts from users that they follow. The collect module allows posts to be turned into collectible NFTs.
- **Mirror**: The Mirror module will allow users to re-share posts, like a retweet on Twitter.
- **Follow**: The follow module will allow users to follow other users. Every time a user follows another user, a unique NFT is issued to the follower. This can allow for additional governance to be implemented into a dApp, as you can see things like who someone's first follower is.
- **Comment**: The comment module allows users to comment on posts.

Since the Lens Protocol is open source as well, you can even create your own modules.

## What else can Lens do?

So, we learned how useful Lens Protocol is for developers and how it also gives the power back to users who want to create and manage their own content. But, what can else Lens do? Using Lens, users can sign into a growing collection of dApps and services that are built on the Lens Protocol.

A great example of a dApp built on Lens Protocol is [Lenster](lenster.xyz) (a Community built, open source social network). Lenster is essentially a decentralized Twitter, allowing users to follow others, post content, and build a community.

The Lens team also created [LensFrens](https://www.lensfrens.xyz/), a dApp that recommends users to follow based on your web3 footprint. This can be super useful when trying to find other people to follow.

These two dApps are just examples of what the Lens Protocol can do. We are still in the early stages of decentralized social media, so more dApps will be created over time to connect to. This is another great thing about the Lens Procol - it's completely open source, so new dApps are currently being built.

There are other many great dApps waiting to be built by **you**! You can check out all the [great dApp ideas](https://ideas.lens.xyz) on the official Lens website.

## Next step: Building with Lens

Now that we've learned a bit about how the Lens Protocol works, we'll learn how to build a Lens-compatible dApp in the next section!

# Building with Lens

## Setup

Fork and clone our starter repo: https://github.com/womenbuildweb3/lens-api-starter

# Get recommended profiles

In the root directory of your project, create a file called `api.js`.

Copy and paste the following variable.

```jsx
export const recommendedProfiles = `
query RecommendedProfiles {
    recommendedProfiles {
      id
      name
      bio
      attributes {
        displayType
        traitType
        key
        value
      }
      followNftAddress
      metadata
      isDefault
      picture {
        ... on NftImage {
          contractAddress
          tokenId
          uri
          verified
        }
        ... on MediaSet {
          original {
            url
            mimeType
          }
        }
        __typename
      }
      handle
      coverPicture {
        ... on NftImage {
          contractAddress
          tokenId
          uri
          verified
        }
        ... on MediaSet {
          original {
            url
            mimeType
          }
        }
        __typename
      }
      ownedBy
      dispatcher {
        address
        canUseRelay
      }
      stats {
        totalFollowers
        totalFollowing
        totalPosts
        totalComments
        totalMirrors
        totalPublications
        totalCollects
      }
      followModule {
        ... on FeeFollowModuleSettings {
          type
          amount {
            asset {
              symbol
              name
              decimals
              address
            }
            value
          }
          recipient
        }
        ... on ProfileFollowModuleSettings {
        type
        }
        ... on RevertFollowModuleSettings {
        type
        }
      }
    }
  }
  `;
```

This query returns several popular profiles.

Let’s configure our urql client so we can call our query. At the top of the file, add

```jsx
import { createClient } from 'urql';

const APIURL = "https://api-mumbai.lens.dev";

export const client = new createClient({
  url: APIURL,
});

```

Next, from your root directory, find the pages folder and go to your `index.js` file. It should look like this:


```jsx
import { useState, useEffect } from "react";
import Link from "next/link";
import Image from "next/image";
import { client, recommendedProfiles } from "../api";

export default function Home() {

  useEffect(() => {
    fetchProfiles();
  }, []);

  async function fetchProfiles() {
    try {
      const response = await client.query(recommendedProfiles).toPromise();
      console.log({ response });
    } catch (err) {
      console.log(err);
    }
  }

  return (
   <div></div>
  );
}

```

In your terminal, run `npm run dev` to run your project locally then visit http://localhost:3000 in your browser. Open your browser’s console to see the data we fetched from our recommendedProfiles query. It should look something like this -

![Screenshot of developer console showing a response object with data for lens profiles](https://i.imgur.com/CopLR6x.png)

Don't worry if a lof of these results have blank fields.

Now that we’ve confirmed that our query is working, let’s display the profiles. Update your Home component to look like this -


```jsx
import { useState, useEffect } from "react";
import Link from "next/link";
import Image from "next/image";
import { client, recommendedProfiles } from "../api";
import Layout from "../components/Layout";
import Head from "next/head";
import HeroSection from "../components/HeroSection";

export default function Home() {
  const [profiles, setProfiles] = useState([]);

  useEffect(() => {
    fetchProfiles();
  }, []);

  async function fetchProfiles() {
    try {
      const response = await client.query(recommendedProfiles).toPromise();
      console.log({ response });
      setProfiles(response.data.recommendedProfiles);
    } catch (err) {
      console.log(err);
    }
  }

  return (
    <Layout>
      <Head>
        <title>Lensbook</title>
      </Head>
      <HeroSection />
      <div className="my-16 space-y-12 sm:grid sm:grid-cols-2 sm:gap-x-6 sm:gap-y-12 sm:space-y-0 md:grid-cols-3 md:gap-x-8 lg:grid-cols-4">
        {profiles.map((profile) => (
          <Link href={`/profile/${profile.id}`} key={profile.id}>
            <a className="flex flex-col items-center">
              {profile.picture &&
              profile.picture.original &&
              profile.picture.original.url.includes("lens.infura-ipfs.io") ? (
                <div className="relative w-60 h-60 bg-emerald-900 rounded">
                  <Image
                    src={profile.picture.original.url}
                    layout="fill"
                    objectFit="cover"
                    alt={profile.handle}
                    className="rounded"
                  />
                </div>
              ) : (
                <div className="bg-emerald-900 w-60 h-60 rounded" />
              )}
              <div className="mt-4 text-lg leading-6 font-medium text-center space-y-1">
                <h3>{profile.name}</h3>
                <p className="text-emerald-600">{profile.handle}</p>
              </div>
              <div className="text-gray-600 mt-2 grid grid-cols-2 gap-x-2 text-sm sm:text-base text-center">
                <p>
                  <span className="text-gray-900 font-medium">
                    {profile.stats.totalFollowers}
                  </span>{" "}
                  Followers
                </p>
                <p>
                  <span className="text-gray-900 font-medium">
                    {profile.stats.totalFollowing}
                  </span>{" "}
                  Following
                </p>
              </div>
            </a>
          </Link>
        ))}
      </div>
    </Layout>
  );
}
```

Your homepage should now show a list of popular lens profiles.

# View a profile

Next, we can create a page that will show the details of any profile we click on. To do this, we can use dynamic routing. Create a new folder inside the pages folder named `profile`. Inside this folder, create a new file called `[id].js` and add the code below.

```jsx
import { useRouter } from "next/router";
import Layout from "../../components/Layout";
import Head from "next/head";
import Image from "next/image";

export default function Profile() {
  const router = useRouter();
  const { id } = router.query;
  return (
    <div>
      ID: {id}
    </div>
  );
}
```
Now if you click on any profile on your homepage, you should see that user's id on the profile page.

We will want to show more details about the user here, so we’ll need to set up a query to fetch that data. 

In our `api.js` file, we can add a new query called getProfileById. We copied this query from the Lens docs here: https://docs.lens.xyz/docs/get-profile

```jsx
export const getProfileById = `
    query Profile($id: ProfileId!) {
     profile(request: { profileId: $id }) {
       id
       name
       bio
       attributes {
         displayType
         traitType
         key
         value
       }
       followNftAddress
       metadata
       isDefault
       picture {
         ... on NftImage {
           contractAddress
           tokenId
           uri
           verified
         }
         ... on MediaSet {
           original {
             url
             mimeType
           }
         }
         __typename
       }
       handle
       coverPicture {
         ... on NftImage {
           contractAddress
           tokenId
           uri
           verified
         }
         ... on MediaSet {
           original {
             url
             mimeType
           }
         }
         __typename
       }
       ownedBy
       dispatcher {
         address
         canUseRelay
       }
       stats {
         totalFollowers
         totalFollowing
         totalPosts
         totalComments
         totalMirrors
         totalPublications
         totalCollects
       }
       followModule {
         ... on FeeFollowModuleSettings {
           type
           amount {
             asset {
               symbol
               name
               decimals
               address
             }
             value
           }
           recipient
         }
         ... on ProfileFollowModuleSettings {
           type
         }
         ... on RevertFollowModuleSettings {
           type
         }
       }
     }
    }
`;
```

Back in our `/pages/profile/[id].js` file, we can import our client and query at the top of the file, as well as `useState` and `useEffect`.

```jsx
import { useState, useEffect } from "react"
import { client, getProfileById } from "../../api"
```

Inside your `Profile` function, create an async function called `fetchProfile`, where we can fetch our data. We can use `useEffect` to run this function if the `id` variable isn’t null. Make sure to add the `id` to the dependency array.

```jsx
  useEffect(() => {
    if (id) {
      fetchProfile();
    }
  }, [id]);
 
async function fetchProfile() {
      
}
```

In the fetchProfile function, we can use a try…catch statement to catch any errors while fetching the profile data.

```jsx
async function fetchProfile(){
       try{
           const response = await client.query(getProfileById, { id }).toPromise()
           console.log("PROFILE:", response)
       } catch(error){
           console.log("ERROR:", error)
       }
   }
```

Now you should be able to see the profile data logged in the console. 

Next, instead of just logging this data, you can save it to the state. At the top of the `Profile` function, use the `useState` hook to declare the variables `profile` and `setProfile`:

```jsx
const [profile, setProfile] = useState()
```

Now you can use `setProfile` to store the profile data from your response. To test it out, you can try rendering the handle of the profile if the profile variable isn’t null.

```jsx
async function fetchProfile(){
       try{
           const response = await client.query(getProfileById, { id }).toPromise()
           console.log("PROFILE:", response)
           setProfile(response.data.profile)
       } catch(error){
           console.log("ERROR:", error)
       }
   }
 
   return (
       <div>
           {profile && <div>{profile.handle}</div>}
       </div>
   )
```

Next, you can update our profile page UI to display more profile information. We added some styling here for you too to make the page look nicer.


```jsx
return (
    <Layout>
      <Head>
        <title>{profile ? profile.handle : "Lensbook"}</title>
      </Head>
      <div className="my-12">
        {profile && (
          <div className="flex flex-wrap md:flex-nowrap items-start w-full">
            <div className="w-full md:w-auto mb-4 md:mr-8">
              {profile.picture &&
              profile.picture.original &&
              profile.picture.original.url.includes("lens.infura-ipfs.io") ? (
                <div className="relative w-60 h-60 bg-emerald-900 rounded mx-auto">
                  <Image
                    src={profile.picture.original.url}
                    layout="fill"
                    objectFit="cover"
                    alt={profile.handle}
                    className="rounded"
                  />
                </div>
              ) : (
                <div className="bg-emerald-900 w-60 h-60 rounded mx-auto" />
              )}
            </div>
            <div className="grow-1 w-full">
              <div className="text-center md:text-left">
                <h1 className="text-3xl font-bold text-gray-900 sm:text-4xl sm:tracking-tight mb-1">
                  {profile.name}
                </h1>
                <h2 className="text-xl font-bold text-emerald-500 sm:text-2xl sm:tracking-tight mb-2">
                  {profile.handle}
                </h2>
                <div className="flex flex-wrap gap-x-2 text-gray-600 text-sm sm:text-base mb-4 justify-center md:justify-start">
                  <p>
                    <span className="text-gray-900 font-medium">
                      {profile.stats.totalFollowers}
                    </span>{" "}
                    Followers
                  </p>
                  <p>
                    <span className="text-gray-900 font-medium">
                      {profile.stats.totalFollowing}
                    </span>{" "}
                    Following
                  </p>
                </div>
                <p className="mb-4">{profile.bio}</p>
                {/* Add connect and follow buttons here */}
              </div>
              {/* Add publications here */}
            </div>
          </div>
        )}
      </div>
    </Layout>
)
```

Next, we want to be able to show all of the publications this user created. From the Lens docs, “Publications come in three primary types: posts, comments, and mirrors. Posts are the base object, with mirror and comment providing additional functionality.” If you’re familiar with Twitter, think of posts like tweets and mirrors like retweets. Publications would be like the Tweets & Replies section of a someone's Twitter profile. 

Back in our api.js file, we can add this new query to fetch the publications for a certain user. Here we name it getPublicationsById. Note: we copied this query from the lens docs here: https://docs.lens.xyz/docs/get-publications. This is a pretty large query, and you probably won’t want to use all of this data. We recommend removing any fields you don’t end up using.

```jsx
export const getPublicationsById = `
    query Publications ($id: ProfileId!) {
     publications(request: {
       profileId: $id,
       publicationTypes: [POST, COMMENT, MIRROR],
       limit: 10
     }) {
       items {
         __typename
         ... on Post {
           ...PostFields
         }
         ... on Comment {
           ...CommentFields
         }
         ... on Mirror {
           ...MirrorFields
         }
       }
       pageInfo {
         prev
         next
         totalCount
       }
     }
    }

    fragment MediaFields on Media {
     url
     mimeType
    }

    fragment ProfileFields on Profile {
     id
     name
     bio
     attributes {
        displayType
        traitType
        key
        value
     }
     isFollowedByMe
     isFollowing(who: null)
     followNftAddress
     metadata
     isDefault
     handle
     picture {
       ... on NftImage {
         contractAddress
         tokenId
         uri
         verified
       }
       ... on MediaSet {
         original {
           ...MediaFields
         }
       }
     }
     coverPicture {
       ... on NftImage {
         contractAddress
         tokenId
         uri
         verified
       }
       ... on MediaSet {
         original {
           ...MediaFields
         }
       }
     }
     ownedBy
     dispatcher {
       address
     }
     stats {
       totalFollowers
       totalFollowing
       totalPosts
       totalComments
       totalMirrors
       totalPublications
       totalCollects
     }
     followModule {
       ... on FeeFollowModuleSettings {
         type
         amount {
           asset {
             name
             symbol
             decimals
             address
           }
           value
         }
         recipient
       }
       ... on ProfileFollowModuleSettings {
        type
       }
       ... on RevertFollowModuleSettings {
        type
       }
     }
    }

    fragment PublicationStatsFields on PublicationStats {
     totalAmountOfMirrors
     totalAmountOfCollects
     totalAmountOfComments
    }

    fragment MetadataOutputFields on MetadataOutput {
     name
     description
     content
     media {
       original {
         ...MediaFields
       }
     }
     attributes {
       displayType
       traitType
       value
     }
    }

    fragment Erc20Fields on Erc20 {
     name
     symbol
     decimals
     address
    }

    fragment CollectModuleFields on CollectModule {
     __typename
     ... on FreeCollectModuleSettings {
         type
         followerOnly
         contractAddress
     }
     ... on FeeCollectModuleSettings {
       type
       amount {
         asset {
           ...Erc20Fields
         }
         value
       }
       recipient
       referralFee
     }
     ... on LimitedFeeCollectModuleSettings {
       type
       collectLimit
       amount {
         asset {
           ...Erc20Fields
         }
         value
       }
       recipient
       referralFee
     }
     ... on LimitedTimedFeeCollectModuleSettings {
       type
       collectLimit
       amount {
         asset {
           ...Erc20Fields
         }
         value
       }
       recipient
       referralFee
       endTimestamp
     }
     ... on RevertCollectModuleSettings {
       type
     }
     ... on TimedFeeCollectModuleSettings {
       type
       amount {
         asset {
           ...Erc20Fields
         }
         value
       }
       recipient
       referralFee
       endTimestamp
     }
    }

    fragment PostFields on Post {
     id
     profile {
       ...ProfileFields
     }
     stats {
       ...PublicationStatsFields
     }
     metadata {
       ...MetadataOutputFields
     }
     createdAt
     collectModule {
       ...CollectModuleFields
     }
     referenceModule {
       ... on FollowOnlyReferenceModuleSettings {
         type
       }
     }
     appId
     hidden
     reaction(request: null)
     mirrors(by: null)
     hasCollectedByMe
    }

    fragment MirrorBaseFields on Mirror {
     id
     profile {
       ...ProfileFields
     }
     stats {
       ...PublicationStatsFields
     }
     metadata {
       ...MetadataOutputFields
     }
     createdAt
     collectModule {
       ...CollectModuleFields
     }
     referenceModule {
       ... on FollowOnlyReferenceModuleSettings {
         type
       }
     }
     appId
     hidden
     reaction(request: null)
     hasCollectedByMe
    }

    fragment MirrorFields on Mirror {
     ...MirrorBaseFields
     mirrorOf {
      ... on Post {
         ...PostFields         
      }
      ... on Comment {
         ...CommentFields         
      }
     }
    }

    fragment CommentBaseFields on Comment {
     id
     profile {
       ...ProfileFields
     }
     stats {
       ...PublicationStatsFields
     }
     metadata {
       ...MetadataOutputFields
     }
     createdAt
     collectModule {
       ...CollectModuleFields
     }
     referenceModule {
       ... on FollowOnlyReferenceModuleSettings {
         type
       }
     }
     appId
     hidden
     reaction(request: null)
     mirrors(by: null)
     hasCollectedByMe
    }

    fragment CommentFields on Comment {
     ...CommentBaseFields
     mainPost {
       ... on Post {
         ...PostFields
       }
       ... on Mirror {
         ...MirrorBaseFields
         mirrorOf {
           ... on Post {
              ...PostFields         
           }
           ... on Comment {
              ...CommentMirrorOfFields       
           }
         }
       }
     }
    }

    fragment CommentMirrorOfFields on Comment {
     ...CommentBaseFields
     mainPost {
       ... on Post {
         ...PostFields
       }
       ... on Mirror {
          ...MirrorBaseFields
       }
     }
    }
    `;
```

Now in `pages/profile/[id].js` you can update the import at the top to include this function.
```jsx
import { client, getProfileById, getPublicationsById } from "../../api"
```

You will also add a new variable to keep track of our publications. To keep it short, we’ll call it pubs and set it to an empty array with `useState`.
```jsx
const [pubs, setPubs] = useState([])
```
Now you can fetch the publications in your fetchProfile function:
```jsx
async function fetchProfile(){
       try{
           const response = await client.query(getProfileById, { id }).toPromise()
           console.log("PROFILE:", response)
           setProfile(response.data.profile)
 
           const publications = await client.query(getPublicationsById, { id }).toPromise()
           console.log("PUBS!", publications)
           setPubs(publications.data.publications.items)
       } catch(error){
           console.log("ERROR:", error)
       }
   }
```

Next, you can map the publications to show a list of each one:

```jsx
{/* Add publications here */}
  {pubs.length > 0 && (
    <div className="border-t-2 border-gray-100 my-8 py-8 flex flex-col space-y-8">
      {pubs.map((p, index) => (
        <div key={p.id}>
          <p className="font-bold">{p.__typename}</p>
          <p>{p.metadata.content}</p>
          <p>{p.metadata.name}</p>
        </div>
      ))}
    </div>
  )}

```

# Set up your wallet

Before we continue, we want to make sure we have a wallet that can connect to Polygon testnet and has some Matic, so we can interact with Lens smart contracts.

If you haven't set up your wallet yet, visit our tutorial here: https://www.30daysofweb3.xyz/en/curriculum/1-getting-started/5-set-up-your-wallet

You will also need to have test MATIC in your wallet. You can request test MATIC from the polygon mumbai faucet here: https://faucet.polygon.technology/. Alchemy also powers a free mumbai faucet here: https://mumbaifaucet.com/.


# Follow user

We want to add a follow button so users can follow a profile they like. To follow a profile with Lens Protocol, a user will need to interact with the Lens contract.

You can find all of the deployed contract addresses from Lens Protocol here:
https://docs.lens.xyz/docs/deployed-contract-addresses.

For this tutorial, we will be interacting with LensHub contracts that are deployed on Mumbai Testnet.

In the `pages/profile/[id].js` file, add a variable called `CONTRACT_ADDRESS` and set it to the lens contract address.

```jsx
const CONTRACT_ADDRESS = "0x60Ae865ee4C725cd04353b5AAb364553f56ceF82"
```

Then in the root directory of your project, create a file called `abi.json`.

Open this [link](https://mumbai.polygonscan.com/address/0x8C1f82e8AAD9399f52DcF224b77f33d5c1719241#code), and scroll down to where it says “Contract ABI”.

![Contract ABI](https://i.imgur.com/AbxpwRh.png)



Copy and paste the ABI into `abi.json`. 

In the `pages/profile/[id].js`, import the ABI.

```jsx
import ABI from "../../abi.json";
```

Before we can add the follow button, we will need to enable the user to connect their wallet.

First, create a new state variable to reference the connected wallet.

```jsx
const [accounts, setAccounts] = useState(null);
```

Second, add this connectWallet function after `fetchProfile`.

```jsx
 async function connectWallet() {
   const accounts = await window.ethereum.request({
     method: "eth_requestAccounts",
   });
   console.log("accounts: ", accounts);
   setAccounts(accounts);
 }

```

Then in your UI, add a Connect Wallet button that calls the `connectWallet` function on click.

```jsx
{/* Add connect and follow buttons here */}
                
<button
onClick={connectWallet}
type="button"
className="inline-flex items-center px-4 py-2 border border-transparent text-base font-medium rounded-md text-emerald-700 bg-emerald-100 hover:bg-emerald-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-emerald-500"
>
    Connect Wallet
</button>
                
```

Save your file then confirm that you can connect your wallet by clicking Connect Wallet button and seeing the accounts array in your console.

Now that the user can connect their wallet, we can support follow functionality.

Import ethers at the top of the file.

```jsx
import { ethers } from 'ethers'
```

If you recall from earlier in the curriculum, ethers.js is a JavaScript library that allows developers to interact with Ethereum. If you'd prefer, [Alchemy](https://docs.alchemy.com/reference/alchemy-sdk-quickstart) has a SDK that is a superset of several Ethers.js libraries.

Next, add this `followUser` function after `connectWallet`.

```jsx
 async function followUser() {
   const provider = new ethers.providers.Web3Provider(window.ethereum);
   const signer = provider.getSigner();
 
   const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer);
 
   try {
     const tx = await contract.follow([id], [0x0]);
     await tx.wait();
     console.log("Followed user successfully");
   } catch (err) {
     console.log("Failed to follow user due to", err);
   }
 }
```

Let’s go through this function line by line.

First, we create a provider that allows us to connect to the Ethereum network through the user's wallet.

```jsx
const provider = new ethers.providers.Web3Provider(window.ethereum);
```

Next, we get the signer from the provider which will be used to sign and send transactions.

```jsx
const signer = provider.getSigner();
```

Then we create a contract object so we can interact with the LensHub smart contract.

```jsx
const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer);
```

Next, we want to call a function that will let us follow users. We can browser the functions defined in the LensHub smart contract at https://docs.lens.xyz/docs/functions.

Let’s jump to follow() at https://docs.lens.xyz/docs/functions#follow.

The follow() function takes in profileIds as a parameter. profileIds is an array of IDs of the profile you want to follow. Given an array of profile IDs, the follow() function will then mint a follow NFT for each profile ID.

Back to the followUser function, note that we call the contract’s follow() function and pass in the id of the profile page we’re currently on.

```jsx
const tx = await contract.follow([id], [0x0]);
```

Then we wait for that transaction to complete.

```jsx
await tx.wait();
```

We wrap this in a `try..catch` block in case the transaction fails.

Now that we understand how followUser works, add a Follow button that calls followUser on click below the profile information. Update your code like so -

```jsx
{/* Add connect and follow buttons here */}
                {accounts ? (
                  <button
                    onClick={followUser}
                    type="button"
                    className="inline-flex items-center px-4 py-2 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-emerald-600 hover:bg-emerald-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-emerald-500"
                  >
                    Follow {profile.handle}
                  </button>
                ) : (
                  <button
                    onClick={connectWallet}
                    type="button"
                    className="inline-flex items-center px-4 py-2 border border-transparent text-base font-medium rounded-md text-emerald-700 bg-emerald-100 hover:bg-emerald-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-emerald-500"
                  >
                    Connect Wallet
                  </button>
                )}

```

Here, we check whether we've connected our wallet. If the wallet is connected, we will show the Follow button.

Save your updated file, connect your wallet, and then click Follow. Confirm that you are indeed following this profile by checking your console.

# Wrapping Up

Social media is just one application of a social graph, and now with Lens, you have the tools to build applications that reflect the meaningful relationships we have on-chain. For example, you're not beholden to web2 social media's controls - you can easily just build your own [decentralized Twitter](https://docs.alchemy.com/docs/how-to-create-a-decentralized-twitter-with-lens-protocol).

The BUIDL Accelerator is coming up fast. More details will be presented officially in the coming days, but some alpha:

START BUILDING SOMETHING. In addition to our no-strings-attached grants, we will also be awarding $20K in grants to teams building with Lens. So hop into the server, find a team, and start brainstorming!

