Create a Solana NFT Collection Gallery in React

Create a Solana NFT Collection Gallery in React

In this guide, we'll show you how to create a React application that showcases all of the NFTs in your Solana NFT Collection.

We'll also add a simple Connect Wallet button and Mint button that allows you to mint a new NFT into the collection from the application too!

Below is a preview of what you'll build in this guide:

Preview of the guide

Let's get started!

You can access the full source code for this project on our GitHub.

Deploy NFT Collection using Dashboard

If this is your first time building on Solana, follow our Getting Started with Solana guide to set yourself up with a wallet and some test funds.

When you're ready, head over to our dashboard and connect your Phantom wallet:

Connect wallet

After connecting your wallet, you will see a list of programs available for you to deploy like the image below. Choose NFT Collection.

Choose NFT Collection

After clicking NFT Collection a sidebar like below should appear.

Notice the dropdown and button at the bottom?

Change it to Devnet.

Now fill up your NFT Collection metadata like below.

Add metadata for your contract

Click Deploy Now at the bottom of the sidebar.

Click deploy now

Once your Phantom wallet appears click Approve and wait for the success message at the bottom of the screen to appear.

After following the steps above you should end up in your collection's dashboard.

Now it's time to use our template to build a Solana NFT gallery!

Creating The Project

In order to get started,  initialize a new start template with the command below.

npx thirdweb create app --next --solana --ts

Change into the new directory and open the _app.tsx file from the pages directory.

The first thing we'll do is set our network to devnet since this is where we deployed our program to:

// Change the network to the one you want to use: "mainnet-beta", "testnet", "devnet", "localhost" or your own RPC endpoint
const network: Network = "devnet";

Let's remove the default markup in index.tsx rendering the piece of code below:

import { ConnectWallet } from "@thirdweb-dev/react";
import type { NextPage } from "next";
import styles from "../styles/Home.module.css";

const Home: NextPage = () => {
  return <p>Hello world!</p>;
};

export default Home;

In order to display a gallery of our collection's NFT, we must first fetch the on-chain metadata of our collection.

For this purpose, let's import useProgram, useProgramMetadata, and useNFTs from @thirdweb-dev/react-solana. We'll use these hooks to fetch our collection's data.

Let's also get the current user wallet information by importing the useWallet from @solana/wallet-adapter-react.

import { ConnectWallet } from "@thirdweb-dev/react";
import type { NextPage } from "next";
import styles from "../styles/Home.module.css";

import { useProgram, useProgramMetadata, useNFTs } from '@thirdweb-dev/react/solana';

const PROGRAM_ADDRESS = "<your-program-address-here>";

const Home: NextPage = () => 
  const { data: program } = useProgram(PROGRAM_ADDRESS, "nft-collection");
  const { data: metadata } = useProgramMetadata(program);
  const { data: nfts } = useNFTs(program);
  
  const { publicKey } = useWallet();

  return (
    /* insert markup here */
  );
};

export default Home;

Now let's use the data we retrieved from the hooks to display information on the UI:

import type { NextPage } from "next";
import { WalletMultiButton } from "@solana/wallet-adapter-react-ui";
import { useWallet } from "@solana/wallet-adapter-react";
import {
  useProgram,
  useProgramMetadata,
  useNFTs,
} from "@thirdweb-dev/react/solana";
import Card from "../components/Card";
import MintButton from "../components/MintButton";
import styles from "../styles/Home.module.css";

// Default styles that can be overridden by your app
require("@solana/wallet-adapter-react-ui/styles.css");

const PROGRAM_ADDRESS = "<your-program-address>";

const Home: NextPage = () => {
  const { data: program } = useProgram(PROGRAM_ADDRESS, "nft-collection");
  const { data: metadata, isLoading: loadingMetadata } =
    useProgramMetadata(program);
  const { data: nfts, isLoading } = useNFTs(program);

  const { publicKey } = useWallet();

  return (
    <>
      <div className={styles.container}>
        {loadingMetadata ? (
          <div className={styles.loading}>Loading...</div>
        ) : (
          <>
            <h1 className={styles.h1}>{metadata?.name}</h1>
            <div className={styles.iconContainer}>
              <img
                className={styles.thumbnail}
                src={String(metadata?.image)}
                alt={String(metadata?.name)}
                height={120}
              />
            </div>
            <p className={styles.explain}>{metadata?.description}</p>
          </>
        )}
        <div className={styles.buttons}>
          <WalletMultiButton />
          {publicKey && <MintButton />}
        </div>

        {isLoading ? (
          <p>Loading...</p>
        ) : (
          <main className={styles.gallery}>
            {nfts?.map((nft, idx) => (
              <Card key={idx} nft={nft} />
            ))}
          </main>
        )}
      </div>
    </>
  );
};

export default Home;

Remember to replace "<your-program-address-here>" with your program address you can get from the dashboard:

Copy program address

Notice how we imported two custom components Card and MintButton? Let's create these components now.

Create a folder where we can add our custom components called components in the root of the project. Inside the folder, create two files called Card.tsx and MintButton.tsx. Let's write these now!

Card.tsx

The Card component simply takes the metadata of an NFT as a prop and creates a simple card to display the data fetched for a specific NFT.

The most notable data presented are the name of the NFT and the owner.

import { ThirdwebNftMedia } from "@thirdweb-dev/react";
import { NFT } from "@thirdweb-dev/sdk";
import { FC } from "react";
import styles from "../styles/Home.module.css";

type Props = {
  nft: NFT;
};

// revealed address character count
const REVEALED_COUNT = 4;

const Card: FC<Props> = ({ nft }) => {
  return (
    <div className={styles.card}>
      <ThirdwebNftMedia className={styles.thumbnail} metadata={nft.metadata} />
      <h3>{nft.metadata.name}</h3>
      <p>Owned by</p>
      <p>
        {nft.owner.substring(0, REVEALED_COUNT) +
          "..." +
          nft.owner.substring(nft.owner.length - REVEALED_COUNT)}
      </p>
    </div>
  );
};

export default Card;

MintButton.tsx

The MintButton component simply mints another NFT from the data fetched from the program's metadata using useProgramMetadata and useNFTs.

By using the useMintNFT hook, we can await the transaction, and make the button show Minting... text until the transaction is complete:

import styles from "../styles/Home.module.css";
import {
  useProgram,
  useProgramMetadata,
  useNFTs,
  useMintNFT,
} from "@thirdweb-dev/react/solana";

const PROGRAM_ADDRESS = "<your-program-address-here>";

const MintButton = () => {
  const { data: program } = useProgram(PROGRAM_ADDRESS, "nft-collection");
  const { data: metadata } = useProgramMetadata(program);
  const { data: nfts } = useNFTs(program);
  const { mutateAsync: mintNft, isLoading } = useMintNFT(program);

  const mint = async () => {
    if (!metadata || !nfts) return;

    await mintNft({
      metadata: {
        name: metadata.name + `#${nfts.length + 1}`,
        description: metadata.description,
        image: metadata.image,
      },
    });
  };

  return (
    <button onClick={mint} className={styles.mintButton}>
      {isLoading ? "Minting..." : "Mint"}
    </button>
  );
};

export default MintButton;

Conclusion

That's it! We've built a simple NFT Gallery application where we can show the metadata of each of the NFTs in our collection, as well as a button to mint a new NFT!

If you have any questions or got stuck along the way, jump into our Discord to speak with our team directly!