I agree with Arlyn.
To expand just a bit, you can make a structure which contains all the information necessary, including the model to display in the world. Upon interacting (picking up, using, etc) you would simply reference the structure itself for key information regarding the interactions.
Structure with Name, Description, Icon, Model, Child Class, Holding Player, etc. (already mentioned by Arlyn) – all information you want to hold with this item/object belongs here, so long as it should persist with the object instead of the player.
To spawn this randomly in the world, simply reference the structure for the model, instantiate an actor (probably a custom actor with a mesh and particles, or whatever you prefer), set the model in the actor to that of the structure, and pass the structure itself into the actor. When someone interacts with it, if it should simply be added to their inventory, that logic exists in the class you defined and you would reference a function for this logic. Handle your inventory logic as normal, but also pass in the structure for this item in the inventory. When a player picks up the item, set the Holding Player variable to the player who just picked it up, and when dropping it, unset the variable. For tooltips, simply reference the structure for the name/description and anything else you put in the structure that you’d like your players to see.
Then, you also have the power of creating child classes for items that add buffs/debuffs when carrying the item in inventory as well. A function inside of the class can handle this logic, and you can very easily reference the player who is holding the item because you set Holding Player when the item was picked up.
Basically, the concept is to always have this structure passed wherever this item would be. Whether it’s existing in the world as an actor, or in the inventory of a player, you have this structure available. Want ambient sound/particles while the player is holding the item? Great, the structure can store which sound(s) and particle(s) should fire off. Passing the structure in every step of the way will ensure that you always have all the pertinent information necessary to handle all interactions.
This also makes item pools incredibly easy. You can make a structure which is an array of the item structures (we store this array in a structure so we can reference it anywhere, totally independent of characters, modes, levels, etc.). In this example, it would basically be however you want to structure it. You could have an array of normal items, an array of rare items, epic, legendary, etc. Then have your loot randomization (if you are even doing that) simply reference these arrays. Or, you could make array for certain chests, bosses, etc. The challenge will be consistency across all loot tables, and not having to type in a title/description and all values of the items everywhere that you use it. This is why a lot of games use a master loot table, and then pull from that loot table programmatically. That way, they define an item once, and any changes made to the item will carry over to all areas which reference the item.
A word of caution, since you mentioned replication. Never rely on the client’s version of the structure. Doing so will expose your game to hacks which are incredibly easy to pull off using simple memory manipulation. Always use the server’s information, and replicate that back to the client. If the client picks up a basic item, then changes their memory so the item in the inventory is a Legendary Epic Item of Awesomeness, your game shouldn’t care and should know exactly what item it truly is. I’m sure you know this, and if so I apologize for any redundancy here. However, I like to include details for any future searchers.