Creating your own Non-Fungible Token (NFT) may sound like a complex blockchain engineering task, but with the right tools and guidance, it’s entirely achievable—even for beginners. In this hands-on guide, you’ll learn how to build, deploy, and mint an NFT using just 14 lines of Solidity code. No prior experience with Ethereum or smart contracts is required.
By the end of this tutorial, you’ll have a fully functional NFT stored on the Ethereum Ropsten test network, viewable in your MetaMask wallet. Whether you're a developer exploring Web3 or a creator dipping into digital ownership, this walkthrough delivers practical value.
What Is an NFT?
An NFT—short for Non-Fungible Token—is a unique digital asset verified on a blockchain. Unlike cryptocurrencies such as ETH or BTC, which are interchangeable (fungible), each NFT is one-of-a-kind and cannot be replicated.
NFTs are tokens that we can use to represent ownership of unique items. They let us tokenize things like art, collectibles, even real estate. They can only have one official owner at a time and they're secured by the Ethereum blockchain – no one can modify the record of ownership or copy/paste a new NFT into existence.
This makes NFTs ideal for proving authenticity and ownership in digital spaces, from digital art and music to virtual real estate and in-game items.
Understanding ERC-721: The NFT Standard
Most NFTs follow the ERC-721 standard—a set of rules defined in EIP-721 that govern how non-fungible tokens behave on Ethereum. Any smart contract implementing these methods qualifies as an ERC-721-compliant NFT.
Open-source libraries like OpenZeppelin simplify development by offering pre-audited, reusable implementations of ERC-721. This allows developers to focus on customization rather than reinventing core functionality.
👉 Start building your first NFT with a secure development environment today.
What Does “Minting” an NFT Mean?
Minting refers to the process of publishing a unique token on the blockchain. When you mint an NFT, you're creating a verifiable instance of your smart contract with a distinct identity.
Each NFT includes a tokenURI, which points to metadata describing the asset—such as name, image URL, description, and traits—in JSON format.
Here’s an example of NFT metadata:
{
"name": "Sad Circle",
"description": "A sad circle.",
"image": "https://i.imgur.com/Qkw9N0A.jpeg",
"attributes": [
{ "trait_type": "Shape", "value": "Circle" },
{ "trait_type": "Mood", "value": "Sad" }
]
}There are three main ways to store this metadata:
- On-chain: Data stored directly in the contract (secure but expensive).
- IPFS (InterPlanetary File System): Decentralized file storage (recommended for production).
- Custom API: A simple server endpoint returning JSON (ideal for testing).
For this tutorial, we’ll use a custom API via Next.js to keep things beginner-friendly.
Project Overview: Build & Mint Your Own NFT
In this step-by-step guide, you'll:
- Set up your development environment.
- Write a 14-line Solidity smart contract.
- Deploy it to the Ropsten test network.
- Mint your NFT and view it in MetaMask.
No prior blockchain knowledge is required, though familiarity with JavaScript and Node.js helps.
In future tutorials, we’ll expand this into a full React dApp where you can display and sell your NFTs.
Prerequisites
MetaMask Wallet
You’ll need a crypto wallet to interact with Ethereum. We recommend MetaMask, a free browser extension and mobile app that manages Ethereum accounts.
- Install the MetaMask Chrome extension and mobile app.
- Switch to the Ropsten Test Network.
- Get free test ETH from the Ropsten Faucet using your wallet address.
Note: The desktop version doesn’t display NFTs—use the mobile app to view them later.
Alchemy – Ethereum Node Provider
To communicate with Ethereum, you need access to a node. Running your own is complex, so we’ll use Alchemy, a reliable node-as-a-service platform.
- Sign up at Alchemy.com.
- Create an app on the Ethereum network using the Ropsten testnet.
- Copy your HTTP API key—you’ll use it soon.
Node.js and npm
Ensure you have Node.js and npm installed. If not, follow the official installation guide or use a version manager like nvm.
Initialize Your Project
Create a new project directory:
mkdir nft-project
cd nft-project
mkdir ethereum
cd ethereum
npm init -yInstall Hardhat, a powerful Ethereum development environment:
npm install --save-dev hardhat
npx hardhatSelect “Create an empty hardhat.config.js”. This sets up your configuration file.
Now create a frontend folder for the API:
cd ..
mkdir web
cd web
npx create-next-app@latest .Your project structure should now look like:
nft-project/
├── ethereum/
└── web/Configure Environment Variables
Store sensitive data like API keys securely using .env.
Inside /ethereum, create a .env file:
cd ethereum
touch .env
npm install dotenv --saveAdd your credentials:
DEV_API_URL=your_alchemy_http_key
PRIVATE_KEY=your_metamask_private_key
PUBLIC_KEY=your_metamask_address⚠️ Never commit .env files to version control.Write the Smart Contract (14 Lines of Code)
Inside /ethereum, create two folders:
mkdir contracts scriptsInstall OpenZeppelin’s ERC-721 implementation:
npm install @openzeppelin/contractsCreate EmotionalShapes.sol in /contracts:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract EmotionalShapes is ERC721 {
using Counters for Counters.Counter;
Counters.Counter private _tokenIdCounter;
constructor() ERC721("EmotionalShapes", "ESS") {}
function _baseURI() internal pure override returns (string memory) {
return "https://YOUR_NGROK_URL/api/erc721/";
}
function mint(address to) public returns (uint256) {
require(_tokenIdCounter.current() < 3);
_tokenIdCounter.increment();
_safeMint(to, _tokenIdCounter.current());
return _tokenIdCounter.current();
}
}Code Breakdown
- Uses OpenZeppelin’s
ERC721base contract. _tokenIdCountergenerates sequential IDs (1, 2, 3)._baseURI()defines where metadata lives.mint()allows only 3 NFTs (adjustable)._safeMint()ensures safe token creation.
Set Up the Metadata API with Next.js
Navigate to /web/pages/api and create /erc721/[id].js:
const metadata = {
1: {
name: "Sad Circle",
description: "A sad circle.",
image: "https://i.imgur.com/Qkw9N0A.jpeg",
attributes: [{ trait_type: "Shape", value: "Circle" }, { trait_type: "Mood", value: "Sad" }]
},
2: {
name: "Angry Rectangle",
description: "An angry rectangle.",
image: "https://i.imgur.com/SMneO6k.jpeg",
attributes: [{ trait_type: "Shape", value: "Rectangle" }, { trait_type: "Mood", value: "Angry" }]
},
3: {
name: "Bored Triangle",
description: "A bored triangle.",
image: "https://i.imgur.com/hMVRFoJ.jpeg",
attributes: [{ trait_type: "Shape", value: "Triangle" }, { trait_type: "Mood", value: "Bored" }]
}
};
export default function handler(req, res) {
res.status(200).json(metadata[req.query.id] || {});
}Start the server:
npm run devTest it at http://localhost:3000/api/erc721/1.
Use ngrok to expose it publicly:
./ngrok http 3000Update _baseURI() in your contract with the ngrok HTTPS URL.
Deploy the NFT Contract
Install Hardhat ethers plugin:
npm install @nomiclabs/hardhat-ethers --save-devUpdate hardhat.config.js:
require("dotenv").config();
require("@nomiclabs/hardhat-ethers");
module.exports = {
solidity: "0.8.0",
defaultNetwork: "ropsten",
networks: {
ropsten: {
url: process.env.DEV_API_URL,
accounts: [`0x${process.env.PRIVATE_KEY}`],
},
},
};Compile the contract:
npx hardhat compileCreate /scripts/deploy.js:
async function main() {
const EmotionalShapes = await ethers.getContractFactory("EmotionalShapes");
const emotionalShapes = await EmotionalShapes.deploy();
console.log("Contract deployed to:", emotionalShapes.address);
}
main().catch((error) => {
console.error(error);
process.exit(1);
});Run deployment:
node scripts/deploy.jsCopy the contract address and view it on Ropsten Etherscan.
👉 Secure your wallet and manage digital assets safely with trusted tools.
Mint Your NFT
Create mint.js in /scripts:
require("dotenv").config();
const { ethers } = require("ethers");
const contract = require("../artifacts/contracts/EmotionalShapes.sol/EmotionalShapes.json");
const provider = ethers.getDefaultProvider("ropsten", { alchemy: process.env.DEV_API_URL });
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
const emotionalShapes = new ethers.Contract("YOUR_CONTRACT_ADDRESS", contract.abi, wallet);
async function main() {
const tx = await emotionalShapes.mint(process.env.PUBLIC_KEY);
console.log("Transaction hash:", tx.hash);
}
main();Run it:
node scripts/mint.jsCheck the transaction on Etherscan. Then open MetaMask mobile, go to NFTs > Import NFT, and enter your contract address and token ID (e.g., 1).
Frequently Asked Questions
Can I mint more than 3 NFTs?
Yes! Remove or increase the require(_tokenIdCounter.current() < 3); line in the smart contract before redeploying.
Why use ngrok for the API?
ngrok exposes your local development server to the internet so the blockchain can access your metadata endpoint.
Is this suitable for production?
No. For live projects, store metadata on IPFS or Arweave and audit your contract before deployment.
What is ABI?
ABI (Application Binary Interface) defines how external apps interact with your smart contract—like a REST API for blockchains.
Can I change the NFT image after minting?
No. Once minted, the tokenURI is immutable unless your contract includes upgradeable logic (advanced).
How much does minting cost?
On Ropsten, gas is free with test ETH. On mainnet, costs vary based on network congestion.
With just 14 lines of code, you’ve built and minted your first NFT. This foundation opens doors to creating dynamic marketplaces, generative art projects, or token-gated experiences. Keep experimenting—the possibilities in Web3 are endless.
👉 Explore more Web3 development tools and start building decentralized applications now.