How to fetch NFTs from wallet with Next.js and RainbowKit via Moralis API

How to fetch NFTs from wallet with Next.js and RainbowKit via Moralis API

In this article, we are going through how to setup a Next.js app, connect our wallet using RainbowKit, and fetching the NFTs we own on Polygon mainnet via Moralis API and display them.

What we will do to achieve our goal:

  • Install Next.js
  • Install RainbowKit, wagmi, and ethers
  • Store the address of the connected wallet
  • Make a GET request using Moralis API and get the Polygon NFTs we own

Disclamer This can be done in many different ways, we are exploring one of them - that certainly can be improved

Also: I will share the github repo at the bottom of this article.

Let's dive right in.

Setup a Next.js project

Run the following command

# npm
npx create-next-app@latest
# or yarn
yarn create next-app

Installing RainbowKit to our Next.js app

# npm
npm install @rainbow-me/rainbowkit wagmi ethers
# or yarn
yarn add @rainbow-me/rainbowkit wagmi ethers

Add RainbowKit to our app

Now go to pages/_app.js and replace all the content of the file with the following code

import "../styles/globals.css";

import "@rainbow-me/rainbowkit/styles.css";

import {
  getDefaultWallets,
  RainbowKitProvider,
  darkTheme,
} from "@rainbow-me/rainbowkit";
import { chain, configureChains, createClient, WagmiConfig } from "wagmi";
import { alchemyProvider } from "wagmi/providers/alchemy";
import { publicProvider } from "wagmi/providers/public";

const { chains, provider } = configureChains(
  [
    chain.mainnet,
    chain.goerli,
  ],
  [alchemyProvider({ apiKey: process.env.ALCHEMY_ID }), publicProvider()]
);

const { connectors } = getDefaultWallets({
  appName: "Fetch My NFTs",
  chains,
});

const wagmiClient = createClient({
  autoConnect: true,
  connectors,
  provider,
});

function MyApp({ Component, pageProps }) {
  return (
    <WagmiConfig client={wagmiClient}>
      <RainbowKitProvider theme={darkTheme()} chains={chains}>
        <Component {...pageProps} />
      </RainbowKitProvider>
    </WagmiConfig>
  );
}

export default MyApp;

We start with importing the styles from RainbowKit, along with some RainbowKit and Wagmi functions that lets us configure our chains.

configureChains accepts an array of chains and an array of providers, so we configure the chains we'd like to support. This will of course vary depending on which networks you'll focus on but in our case I have added the Ethereum Mainnet, Goerli Testnet and Rinkeby Testnet.

Then we setup a wagmi client, passing autoConnect and setting it to true so that our app automatically reconnects.

And then, we wrap our application with RainbowKitProvider and WagmiConfig.

Add the connect button

Now let's import and render the Connect Wallet button. Go to pages/index.js and replace all the content of the file with the following code

import { ConnectButton } from "@rainbow-me/rainbowkit";
import styles from "../styles/Home.module.css";
import Nav from "../components/nav";
import { useAccount } from "wagmi";
import { useEffect, useState } from "react";

import LoggedIn from "../components/loggedIn";

export default function Home() {
  const { isConnected } = useAccount();
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  useEffect(() => {
    if (!isConnected) {
      setIsLoggedIn(true);
    } else {
      setIsLoggedIn(false);
    }
  }, [isConnected]);
  return (
    <section className={styles.container}>
      <Nav />
      {isLoggedIn ? (
        <main className={styles.main}>
          <h1 className={styles.title} style={{ marginBottom: "4rem" }}>
            Connect your Metamask and see your <span>Polygon</span> owned <span>NFT&apos;s</span>
          </h1>
          <ConnectButton />
        </main>
      ) : (
        <LoggedIn />
      )}
    </section>
  );
}

Don't worry if you get lots of errors here. It is because we are importing components and styling that we haven't created yet.

We start by checking if the user has connected their wallet. If not, we render the welcome text along with a 'Connect your wallet'-button. If they already connected their wallet, we render the LoggedIn components, which we'll soon create.

Let's add some styling to our app, go to styles/Home.module.css and replace all the content of the file with the following code

.container {
  display: flex;
  flex-direction: column;
}

.main {
  min-height: 100vh;
  padding: 10rem 0;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.title span {
  color: #e78db3;
  text-decoration: none;
}

.title {
  margin: 0;
  line-height: 1.15;
  font-size: 2rem;
  text-align: center;
}

Create components folder and add components

Now in your root folder, create a folder named components and in there create a file called nav.js with the below code.

import styles from "../styles/Nav.module.css";
import Link from "next/link";
import { ConnectButton } from "@rainbow-me/rainbowkit";
import { useAccount } from "wagmi";
import { useEffect, useState } from "react";

export default function Nav() {
  const { isConnected } = useAccount();
  const [isLoggedIn, logIn] = useState(false);

  useEffect(() => {
    if (isConnected) {
      logIn(true);
    } else {
      logIn(false);
    }
  }, [isConnected]);

  return (
    <div className={styles.navContainer}>
      <Link href="/">
        <h1 className={styles.title}>
          <span className={`${styles.titleWord} ${styles.titleWord1}`}>MY</span>
          <span className={`${styles.titleWord} ${styles.titleWord2}`}>
            NFT
          </span>
        </h1>
      </Link>
      {isLoggedIn && (
        <ul className={styles.navItems}>
          <li>
            <ConnectButton />
          </li>
        </ul>
      )}
    </div>
  );
}

In this components we add our logo with some styling in.

Add the styling to the nav component by creating a Nav.module.css file inside the styles folder, and add the following

.navContainer {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
    height: 4rem;
    padding: 0 2rem;
  }

  h1.title {
    font-size: 3rem;
    text-decoration: none;
    cursor: pointer;
  }

  .navItems {
    list-style: none;
    cursor: pointer;
  }

  .titleWord {
    animation: color-animation 4s linear infinite;
  }

  .titleWord1 {
    --color-1: #df8453;
    --color-2: #3d8dae;
    --color-3: #e4a9a8;
  }

  .titleWord2 {
    --color-1: #dbad4a;
    --color-2: #accfcb;
    --color-3: #17494d;
  }

  @keyframes color-animation {
    0% {
      color: var(--color-1);
    }
    32% {
      color: var(--color-1);
    }
    33% {
      color: var(--color-2);
    }
    65% {
      color: var(--color-2);
    }
    66% {
      color: var(--color-3);
    }
    99% {
      color: var(--color-3);
    }
    100% {
      color: var(--color-1);
    }
  }

Now we will add a new component called loggedIn.js in the components folder. This component is the one that renders if a wallet is connected.

import styles from "../styles/LoggedIn.module.css";
import FetchData from "./fetchData";

export default function LoggedIn() {
  return (
    <section className={styles.loggedInMain}>
        <section className={styles.loggedInAccount}>
          <FetchData />
        </section>
      </section>
  );
}

And let's add some styling before we go through what happens in here. Create LoggedIn.module.css in the styles folder.

.loggedInMain {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.loggedInAccount {
  display: flex;
  flex-direction: row;
  justify-content: space-evenly;
  width: 65rem;
}

The loggedIn.js component will render all the data we want to show. We are using this component as a parent to everything else so that we can handle what we show when a wallet is connected and when not.

If you want to see the app we have created so far, you should be able to comment out line 2 and 8, and just add a p tag to test it out

Now, let's continue with the actual fetching of the NFTs using Moralis API, and displaying the NFTs.

Remember that we only fetch the NFTs that are owned by the connected wallet, and that are on Polygon mainnet. You could easily change these preferences by checking the docs of Moralis API, which in my opinion is really well documented.

We don't need to register for an API-key. So let's create a file called fetchData.js inside the components folder - and add the following code.

import { useAccount } from "wagmi";
import { useEffect, useState } from "react";
import Card from "./card";
import styles from "../styles/FetchData.module.css";

export default function FetchData() {
  const { address } = useAccount();
  const [data, setData] = useState([]);

    const options = {
      method: "GET",
      headers: {
        Accept: "application/json",
        "X-API-Key": "test",
      },
    };

    useEffect(() => {
      fetch(
        `https://deep-index.moralis.io/api/v2/${address}/nft?chain=polygon&format=decimal`,
        options
      )
        .then((response) => response.json())
        .then((response) => {
          setData(response.result);
        })
        .catch((err) => console.error(err));
    }, [])

  return (
    <section className={styles.dataContainer}>
      {data.map(nft => {
        return (
          <Card uri={nft} key={nft.block_number_minted} />
        )
      })}
    </section>
  );
}

Again we use wagmi to get the address of the connected wallet, and we use that to make a get request from moralis. In the headers we specify that our api key is test, which is taken from the docs of moralis.

We do the fetching inside useEffect hook, which runs after every render. So as soon as user connects their wallet, we fetch from the api, and then store the results.

The mapping can be made in different ways but in this case we pass the results down to the card component (which we'll create soon) and there we parse it to destruct the values we want. But first, let's add a new file called FetchData.module.css in styles folder.

.dataContainer {
  display: flex;
  flex-wrap: wrap;
  width: 60rem;
}

Now, let's create the last file in our components folder and name it card.js. Let's add this code below as well.

import { useState } from "react";
import styles from "../styles/Card.module.css";

export default function Card(props) {
  const [nft, setNft] = useState(JSON.parse(props.uri.metadata))
  const [nftImage, setNftImage] = useState(() => {
    if (nft?.image) {
        return nft.image.includes('ipfs') ? 
        `https://ipfs.io/ipfs/${nft.image.split('ipfs://')[1]}` :
          nft.image.split('\\')[0];
        }
    }
  )

  return (
    <section className={styles.cardContainer}>
      {nft?.name ? (
        <h1>{nft.name}</h1>
      ) : (
        <h1>No NFT title can be shown.</h1>
      )}
      {nftImage ? (
        <img src={nftImage} />
      ) : (
        <p>No NFT image can be shown.</p>
      )}
    </section>
  );
}

We take in the prop that was passed from fetchData.js and we store the parsed version of it in state. After that, we want to format the image link depending on how it looks and pass that value to nftImage. And in the return section we structure how the Card component will render for every NFT we could fetch.

Let's add some styling in styles/Card.module.css

.cardContainer {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 25rem;
  height: 20rem;
  border: 0.5px solid #9b9b9b;
  border-radius: 7px;
  margin: 2rem;
}

.cardContainer h1 {
    color: #e78db3;
    font-size: 1.2rem;
}

.cardContainer img {
  width: 20rem;
  height: 11.2rem;
}

And that should be it.

Now run npm run dev or yarn dev and open localhost:3000 to test the application. It should look like this - this is the deployed version on vercel that you can try.

Here's the github repo.


Thank you for reading through and reaching the end of this article!

If you like this article, please consider sharing this article with your friends and other people that would benefit from it and the technology we use.

Drop a comment down below if you have any questions or want to tell us something!