Improved version of the ERC721 smart contract code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";

contract ERC721Example is Initializable, ERC721Upgradeable, OwnableUpgradeable {

    uint256 private _nextTokenId;
    string private _baseUri;

    // @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize(address initialOwner, string memory name, string memory symbol, string memory baseUri) initializer public {
        __ERC721_init(name, symbol);
        __Ownable_init();
        _baseUri = baseUri;
        transferOwnership(initialOwner);
    }

    // Override _baseURI function to return the base URI for token metadata
    function _baseURI() internal view override returns (string memory) {
        return _baseUri;
    }

    // Mint function to create a new token
    function mint(address to) external onlyOwner {
        uint256 tokenId = _nextTokenId++;
        _safeMint(to, tokenId);
    }

    // Internal function to update the base URI
    function setBaseURI(string memory baseUri) external onlyOwner {
        _baseUri = baseUri;
    }

    // Return token URI with tokenId appended to base URI
    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        return string(abi.encodePacked(_baseURI(), StringsUpgradeable.toString(tokenId)));
    }
}

Initialization with baseUri: Instead of using extendData to store the base URI, I switched to directly using the baseUri parameter in the initialize function. This simplifies deployment and makes it easier to customize after deployment.
setBaseURIfunction: Added asetBaseURIfunction, allowing the contract owner to change thebaseUriafter the contract is deployed. AddedtokenURIfunction: Updated the token URI for each token based on itstokenId, allowing easy retrieval of metadata for each specific token. Optimized ownership management: Used transferOwnershipininitialize` to transfer ownership of the contract, making the code easier to manage.