I think the “standard” way, i.e. the most common pattern, is to pass a transform and a target_transform parameters to your custom dataset, and use those tranformations on the __getitem__ function.
The implementation 1 of some standard torchvision datasets can give you a better notion, but this would be the general idea:
class CustomDataset(Dataset):
    def __init__(self, path, transform=None, target_transform=None):
        self.samples = discover_samples_from_path(path)
        self.transform = transform
        self.target_transform = target_transform
        # do something here

    def __len__(self):
        return len(self.samples)

    def __getitem__(self, index):
        img, target = self.samples[index]
        ...
        
        if self.transform is not None:
            img = self.transform(img)

        if self.target_transform is not None:
            target = self.target_transform(target)

        return img, target

From there, you would normally just instantiate your dataset, instantiate a dataloader using it, and start rocking. The transformations will be applied every time you get an instance from the dataset, without need of passing any of them to the dataloader.
my_dataset = CustomDataset('./path/to/data/', my_transforms['data'], my_transforms['target'])
my_dataloader = DataLoader(my_dataset, batch_size=2, num_workers=2, ...)

for i, (img, target) in enumerate(my_dataloader):
    # do something here