Create an NFT Gallery using thirdweb and Next.js

Create an NFT Gallery using thirdweb and Next.js

In this guide, we are going to create an NFT gallery where users will be able to see all NFTs from an NFT collection/NFT Drop/Signature drop!

We will also let the user connect their wallet to see if they own any NFTs from the collection.

Before we get started, below are some helpful resources where you can learn more about the tools we're going to be using in this guide.

Let's get started.


I am going to use the Next.js Typescript starter template for this guide.

If you are following along with the guide, you can create a project with the template using the thirdweb CLI:

npx thirdweb create --next --ts

If you already have a Next.js app you can simply follow these steps to get started:

  • Install @thirdweb-dev/react and @thirdweb-dev/sdk and ethers
  • Add MetaMask authentication to the site. You can follow this guide to do this.

By default the network in `_app.tsx` is Mainnet, we need to change it to Mumbai

import type { AppProps } from "next/app";
import { ChainId, ThirdwebProvider } from "@thirdweb-dev/react";

// This is the chainId your dApp will work on.
const activeChainId = ChainId.Mumbai;

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ThirdwebProvider desiredChainId={activeChainId}>
      <Component {...pageProps} />

export default MyApp;

Creating a NFT Drop and batch Uploading NFTs

Let's go ahead and create a Signature Drop and upload a batch of NFTs that users can claim.

To do that, head to the thirdweb dashboard and create a signature drop contract!

Fill out the details and deploy the contract by clicking on Deploy Now.

Once the contract is deployed, we need to set up claim phases. So, click on the Claim Phases tab and create a new claim phase:

Now, let's batch upload some NFTs for the users to mint. For this guide, I am going to use the Shapes batch upload example.

Click on batch upload and upload the images and the CSV/JSON file.

Once they are uploaded you will be able to see the NFTs! To learn more about batch upload check out this guide.

Once everything is done, go to the embed tab and claim some NFTs so we can see them in the gallery!

Start building website

Getting the NFTs

In the pages/index.tsx page we will now use the useContract hook provided to use by the React SDK to get access to the contract like this:

const { contract } = useContract("0x05B8aab3fd77580C29c6510d8C54D9E6be4262d2");

Make sure to update the contract address to the address of the Signature Drop contract you created. After doing this, to get the NFTs from this contract we need to use another hook that will give us the NFTs, called useNFTs:

const { data: nfts, isLoading: loading } = useNFTs(contract?.nft, {
  start: 0,
  count: 10,

Here, we are adding a start and count to the query to get the first 10 NFTs. You can update these values as per your needs! These hooks can be imported from the @thirdweb-dev/react package like this:

import {useAddress,useContract,useMetamask,useNFTs,} from "@thirdweb-dev/react";

Rendering the NFTs

Delete everything inside the main div and let's render our NFTs! To render the NFTs, we need to map through them and render them:

  {nfts && nfts?.length > 0 && (
      { => (
        <div key={}>
          <ThirdwebNftMedia metadata={nft.metadata} />
          <p>owned by {truncateAddress(nft.owner)}</p>

As you can see we are using a function for truncating the address. Add this function:

const truncateAddress = (address: string) => {
  return (
    address.substring(0, 6) + "..." + address.substring(address.length - 4)

Now you would be able to see all the NFTs but the images are huge and the cards are not at all styled!

So let's style the images and the cards using CSS modules. Create a new folder called styles and create a globals.css file inside it. Finally, add the following

body {
  padding: 0;
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu,
    Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;

a {
  color: inherit;
  text-decoration: none;

* {
  box-sizing: border-box;

Now, import it in _app.tsx like this:

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

Let's now create Home.module.css inside the styles folder and add the following:

.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100vw;
  min-height: 100vh;
  background-color: #c0ffee;

.cards {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  margin-top: 20px;
  gap: 20px;
  width: 80vw;

.card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  margin: 10px;
  box-shadow: 0px 0px 5px #000000;
  min-width: 250px;
  padding: 10px;
  background: #1ce;

.image {
  min-width: 100px;
  max-width: 150px;
  min-height: 100px;
  max-height: 150px;
  position: relative;

This is some basic styling for the cards, container, and image. Finally, let's use them in our pages/index.tsx page:

<div className={styles.container}>
  {!address && <button onClick={connectWithMetamask}>Connect Wallet</button>}

  {nfts && nfts?.length > 0 && (
    <div className={}>
      { => (
        <div key={} className={styles.card}>
          <ThirdwebNftMedia metadata={nft.metadata} className={styles.image} />
          <p>owned by {truncateAddress(nft.owner)}</p>

Now our home page looks pretty nice!

Some improvements

Not showing NFTs owned by null address

You might have seen that the NFTs that are owned by 0x0000000000000000000000000000000000000000 are also being shown but we probably won't want to show those because they haven't been minted yet or have been burned. So we can add a simple filter to do this before our map function:

    .filter((nft) => nft.owner !== "0x0000000000000000000000000000000000000000")
    .map((nft) => <>{/* rest of the code */}</>);

This will make sure that only the NFTs that have an owner will be shown.

Adding a loading screen

The NFTs take quite some time to load so before the first return statement we will add this loading screen:

if (loading) {
  return <div className={styles.container}>Loading...</div>;

Showing special text if NFT is owned by you If you remember we had these two hooks present when we created our template:

const address = useAddress();
const connectWithMetamask = useMetamask();

If you have removed them add them back because we are going to need them now!

Now, just before the map function we can add this:

  !address && <button onClick={connectWithMetamask}>Connect Wallet</button>;

Finally, in the p tag where we show the owner address we can add this:

  owned by{" "}
  {address && nft.owner === address ? "you" : truncateAddress(nft.owner)}

Now, we have got an amazing gallery of our NFTs!


Hope you could integrate your own NFT gallery and if you did share it with the community! If you want to have a look at the code, check out the GitHub Repository.