As the NFT area is growing every day, more new mechanics and experiments are coming to the world of priceless pixels. One of the popular approaches during NFT launches is a Reveal toggle functionality. Reveal functionality stands for hiding the NFT picture with some placeholder image initially. In this article we will go through the process of implementing such behavior and analyze why you’d possibly use it in your collection. Theory Firstly, let’s dive into the theory of NFTs to define the steps needed for our Reveal. NFTs are following standard of a non-fungible token and all of the generative collections follow interface as well. ERC721 ERC721Metadata interface ERC721Metadata { function name() external view returns (string _name); function symbol() external view returns (string _symbol); function tokenURI(uint256 _tokenId) external view returns (string); } This interface allows us to specify name, symbol and tokenURI for each NFT in our collection. What we are interested here is function that is responsible for returning item’s metadata value. tokenURI So our task for implementing Reveal is the following: Create a flag variable in Smart Contract that indicates whether NFTs are hidden/revealed Add external function for toggling (or setting to true) flag variable value, that has to be accessed only by owner Override method to return the same single metadata file for every NFT during the Hidden phase and appropriate NFT metadata after reveal tokenURI Upload Hidden phase metadata to IPFS Updating Smart Contract I won’t go deep with explaining the common NFT smart-contract, assuming that you have one already ;) If not, then have a look on standard that provides gas-efficient minting, so your customers won’t pay a lot for transaction fee. I’ll be using ERC721A in this tutorial as well. ERC721A Let’s add the flag property to existing Contract class and initialize it with : false bool public revealed = false; Then let’s add a function for changing our value and make sure it could only be called by the . For doing that, I’d suggest derive from the OpenZeppelin contract that will add modifier for us. revealed owner Ownable onlyOwner contract HiddenReveal is ERC721A, Ownable { bool public revealed = false; function reveal() external onlyOwner {revealed = true;} } As you can see, I’m not toggling the value but just set it . In this way you won’t make your community nervous that you might hide their NFTs again after revealing 🙃 You can make it to toggle current value or set to the value passed into the function. revealed true Now is the most interesting part: overriding function based on our flag. tokenURI revealed function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { if (!_exists(tokenId)) revert URIQueryForNonexistentToken(); string memory baseURI = _baseURI(); string memory metadataPointerId = !revealed ? 'unrevealed' : _toString(tokenId); string memory result = string(abi.encodePacked(baseURI, metadataPointerId, '.json')); return bytes(baseURI).length != 0 ? result : ''; } Check if token with such exists and revert with Custom Error if not tokenId Take the value to concat it with and get metadata URL baseURI tokenId Check the current state and if it’s then use string instead of , store it into revealed false unrevealed tokenId metadataPointerId Concat , and extension baseURI metadataPointerId .json Return the is baseURI is specified result Final result for will be something like this, for every NFT: revealed=false or https://www.mybaseurilink.com/folderipfshash/unrevealed.json ipfs://folderipfshash/unrevealed.json Here is the full Smart Contract code with Hide/Reveal functionality: contract HiddenReveal is ERC721A, Ownable { string private _baseTokenURI; bool public revealed = false; function _baseURI() internal view virtual override returns (string memory) { return _baseTokenURI; } function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { if (!_exists(tokenId)) revert URIQueryForNonexistentToken(); string memory baseURI = _baseURI(); string memory metadataPointerId = !revealed ? 'unrevealed' : _toString(tokenId); string memory result = string(abi.encodePacked(baseURI, metadataPointerId, '.json')); return bytes(baseURI).length != 0 ? result : ''; } function setBaseURI(string calldata baseURI) external onlyOwner { _baseTokenURI = baseURI; } function reveal() external onlyOwner { revealed = true; } } Please, note that this Smart Contract is NOT production ready, as it doesn’t have mint functions lol. But you can use it as a reference to plug functionality into your solution Setting Up IPFS After finishing with Smart Contract part, you have to set up the storage for your Unrevealed items metadata. Generally, you will just need to store a placeholder image and metadata files in a separate IPFS folder. unrevealed.json Then you connect your Smart Contract to the IPFS storage through calling on contract with the folder’s hash. setBaseURI Here is an example of : unrevealed.json { "name": "@sir_fedos Hide/Reveal", "description": "Tutorial on how to create Reveal functionality on your NFT", "image": "ipfs://hAshToYoUrPlAcEhoLderImAge" } Conclusion All in all, Reveal functionality is a great marketing tool and a tiny mechanic that could be added to every NFT drop with ease. Moreover, you can specify multiple reveal stages, define them as an enum and add some entertainment to the post-mint phase of your project! Follow for more tutorials! Also published . here