Create An NFT Gated Website

Create An NFT Gated Website

In this guide, we'll show you how to create a website that restricts content based on owning an NFT with React.

In this guide, we use an Edition Drop, which allows many NFTs to be created from the same asset, which is perfect for a membership card!

Using the template repository

To get started, we can use the CLI to clone a starter kit.

npx thirdweb create --next --js

Setup the ThirdwebProvider

Inside the pages/_app.js page, we wrap our application in the ThirdwebProvider component to access all of the React SDK's hooks anywhere in our application.

In this file, we need to configure a desiredChainId that our project will work on.

For this project, we’ll be using Mumbai since our Edition Drop contract is on the Mumbai testnet.

import { ChainId, ThirdwebProvider } from "@thirdweb-dev/react";

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

function MyApp({ Component, pageProps }) {
  return (
    <ThirdwebProvider
      desiredChainId={activeChainId}
      authConfig={{
        domain: "example.org",
        authUrl: "/api/auth",
        loginRedirect: "/",
      }}
    >
      <Component {...pageProps} />
    </ThirdwebProvider>
  );
}

export default MyApp;

Creating the Auth Config File

Create a new file called auth.config.js at the root of your project.

Here is where we configure the ThirdwebAuth object for us to use in our authentication endpoints.

We need to pass two configuration options to the ThirdwebAuth object:

  1. Our wallet's privateKey, which is used on the server-side to generate signatures for users to sign into the application with.
  2. Our domain name, which can be any value you want, typically the name of your website such as thirdweb.com.
import { ThirdwebAuth } from "@thirdweb-dev/auth/next";

export const { ThirdwebAuthHandler, getUser } = ThirdwebAuth({
  privateKey: "<your-private-key-here>",
  domain: "your-domain-name-here",
});

Learn how to export your private key from your wallet.

Ensure you store and access your private key securely.

  • Never commit any file that may contain your private key to your source control.

Learn more about securely accessing your private key.

Auth API Endpoint

Finally, we have a catch-all API route
called pages/api/auth/[...thirdweb].js, which exports the ThirdwebAuthHandler to manage all of the required auth endpoints like login and logout.

Create a new page inside the pages/api/auth folder (you'll need to create this) and name it [...thirdweb].js,
and export the ThirdwebAuthHandler from this file that we set up in our auth.config.js file.

import { ThirdwebAuthHandler } from "../../../auth.config";

export default ThirdwebAuthHandler();

Restricting Home Page Access

On the homepage, we run server-side code within getServersideProps to check:

  1. The user is authenticated
  2. The user has an NFT from our collection

If the user isn't authenticated or doesn't have an NFT, they're redirected to the /login page before they can access
the content of the home page.

Let's set up this home page logic now.

First, we need to import the functionality we're going to be using:

import React from "react";
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
import { useLogout } from "@thirdweb-dev/react";
import { getUser } from "../auth.config";
import checkBalance from "../util/checkBalance";

Since our home page is only accessible by authenticated NFT holders, we can show any data here, such as a thank you message

export default function Home() {
  const logout = useLogout();

  return (
    <div>
      <h1>Restricted Access Page</h1>
      <p>Thanks for being a member of our NFT community!</p>

      <button onClick={logout}>Logout</button>
    </div>
  );
}

Above, we're showing a simple thank you message and a button to logout, which will sign them out and redirect the user to the /login page.

Before we reach this point, we'll run logic inside out getServersideProps function,
which checks the user's authentication status and their NFT balance.

If they are not authenticated or don't own an NFT, they are redirected back to the /login page.

// This gets called on every request
export async function getServerSideProps(context) {
  const user = await getUser(context.req);

  if (!user) {
    return {
      redirect: {
        destination: "/login",
        permanent: false,
      },
    };
  }

  // Instantiate our SDK
  const sdk = ThirdwebSDK.fromPrivateKey(
    // Learn more about securely storing your private key: https://portal.thirdweb.com/web3-sdk/set-up-the-sdk/securing-your-private-key
    "your-admin-private-key-here"
    "mumbai",
  );

  // Check to see if the user has an NFT
  const hasNft = await checkBalance(sdk, user.address);

  // If they don't have an NFT, redirect them to the login page
  console.log("User", user.address, "doesn't have an NFT! Redirecting...");
  if (!hasNft) {
    return {
      redirect: {
        destination: "/login",
        permanent: false,
      },
    };
  }

  // Finally, return the props
  return {
    props: {},
  };
}

Checking NFT Balance

In the snippet above, we call a utility function called checkBalance, which checks to see if the user has an NFT from our collection.

import { contractAddress } from "../const/yourDetails";

export default async function checkBalance(sdk, address) {
  const editionDrop = sdk.getEditionDrop(
    contractAddress, // replace this with your contract address
  );

  const balance = await editionDrop.balanceOf(address, 0);

  // gt = greater than
  return balance.gt(0);
}

Great! Now each time a user attempts to reach the / page, we'll check to see if they have an NFT before allowing them to enter.

Let's create the /login page now where users can sign into our application with their wallet.

Login Page

Create a new page in the pages folder called login.jsx.

This page needs to contain a button that calls the login function

First, let's import the logic we need for this file:

import {
  useAddress,
  useMetamask,
  useEditionDrop,
  useClaimNFT,
  useNetwork,
  useNetworkMismatch,
  useUser,
  useLogin,
} from "@thirdweb-dev/react";

import { ChainId } from "@thirdweb-dev/sdk";
import { contractAddress } from "../const/yourDetails";

Beneath the imports, we render some conditional logic based on the address variable (which detects the connected wallet address to our site).

If there is a connected wallet, we show the Sign In button, which calls the login function we imported from the useLogin hook.

This asks the user to sign a message with their wallet to prove their identity,
and redirects them to the URL we configured for the loginRedirect field in the ThirdwebProvider; which we set as /.

This means that after the user approves the message, they will be redirected to the / page; where they will be checked to see if they have an NFT again!

export default function Login() {
  // Wallet & Network Information
  const address = useAddress();
  const connectWithMetamask = useMetamask();
  // Hooks to sign in with ethereum (auth SDK)
  const login = useLogin(); // Sign in

  return (
    <div>
      {address ? (
        <>
          <p>Welcome, {address.slice(0, 6)}...</p>

          <button onClick={login}>Sign In</button>
        </>
      ) : (
        <>
          <button onClick={() => connectWithMetamask()}>Connect Wallet</button>
        </>
      )}
    </div>
  );
}

Full Project

You can create a copy of this project from our example repository.

To create a project with all of the code from this guide, you can run:

npx thirdweb create --template nft-gated-website

Join our Discord to stay up to date with the latest updates from the team!