Quick answer: Replicated UObject references on a component don’t replicate the sub-object itself. Override ReplicateSubobjects in C++ (or use Replicated Sub-Objects List on UActorComponent in newer versions).
An InventoryComponent holds an array of UItem* UObjects. The references replicate but the items themselves are null on the client — sub-objects didn’t replicate.
References vs Sub-Objects
Marking a UObject pointer as Replicated means the pointer replicates — the engine looks up the target by NetGUID on the client. If the target is a sub-object Created via NewObject and not registered for replication, the client never receives its data — the pointer resolves to null.
C++ Override
bool UInventoryComponent::ReplicateSubobjects(UActorChannel* Channel, FOutBunch* Bunch, FReplicationFlags* RepFlags)
{
bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);
for (UItem* Item : Items)
{
if (IsValid(Item))
WroteSomething |= Channel->ReplicateSubobject(Item, *Bunch, *RepFlags);
}
return WroteSomething;
}
This tells the engine each frame which UObjects ride along on the component’s replication.
Newer Sub-Object API
Recent UE versions add AddReplicatedSubObject / RemoveReplicatedSubObject on UActorComponent — opt in once on Create, opt out on Destroy, no override needed.
The Item Class Itself
The UItem class needs bReplicates = true in its constructor (or set with SetIsReplicatedByDefault(true)). Without that, even a registered sub-object is treated as non-replicating.
Verifying
Server adds an item. Client’s inventory component shows the item with all properties intact, not a null entry. Modifying the item on server propagates.
“Replicated pointer + replicated sub-object are two separate things. You need both for clients to see the data.”
For Blueprint-only inventory, prefer a struct array over UObject sub-objects — structs replicate natively without the sub-object dance.