diff --git a/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/StrippableBlockRegistry.java b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/StrippableBlockRegistry.java
index ed4ea6aac..1723daf96 100644
--- a/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/StrippableBlockRegistry.java
+++ b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/StrippableBlockRegistry.java
@@ -16,7 +16,6 @@
 
 package net.fabricmc.fabric.api.registry;
 
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
 
@@ -27,6 +26,7 @@ import net.minecraft.block.Block;
 import net.minecraft.state.property.Properties;
 
 import net.fabricmc.fabric.mixin.content.registry.AxeItemAccessor;
+import net.fabricmc.fabric.impl.content.registry.util.ImmutableCollectionUtils;
 
 /**
  * A registry for axe stripping interactions. A vanilla example is turning logs to stripped logs.
@@ -49,14 +49,8 @@ public final class StrippableBlockRegistry {
 	public static void register(Block input, Block stripped) {
 		requireNonNullAndAxisProperty(input, "input block");
 		requireNonNullAndAxisProperty(stripped, "stripped block");
-		Map<Block, Block> strippedBlocks = AxeItemAccessor.getStrippedBlocks();
 
-		if (!(strippedBlocks instanceof HashMap<?, ?>)) {
-			strippedBlocks = new HashMap<>(strippedBlocks);
-			AxeItemAccessor.setStrippedBlocks(strippedBlocks);
-		}
-
-		Block old = strippedBlocks.put(input, stripped);
+		Block old = getRegistry().put(input, stripped);
 
 		if (old != null) {
 			LOGGER.debug("Replaced old stripping mapping from {} to {} with {}", input, old, stripped);
@@ -70,4 +64,8 @@ public final class StrippableBlockRegistry {
 			throw new IllegalArgumentException(name + " must have the 'axis' property");
 		}
 	}
+
+	private static Map<Block, Block> getRegistry() {
+		return ImmutableCollectionUtils.getAsMutableMap(AxeItemAccessor::getStrippedBlocks, AxeItemAccessor::setStrippedBlocks);
+	}
 }
diff --git a/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/VillagerInteractionRegistries.java b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/VillagerInteractionRegistries.java
new file mode 100644
index 000000000..2940b9cf6
--- /dev/null
+++ b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/VillagerInteractionRegistries.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.api.registry;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.minecraft.entity.passive.VillagerEntity;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemConvertible;
+import net.minecraft.util.Identifier;
+import net.minecraft.village.VillagerProfession;
+
+import net.fabricmc.fabric.impl.content.registry.util.ImmutableCollectionUtils;
+import net.fabricmc.fabric.mixin.content.registry.VillagerEntityAccessor;
+import net.fabricmc.fabric.mixin.content.registry.FarmerWorkTaskAccessor;
+import net.fabricmc.fabric.mixin.content.registry.GiveGiftsToHeroTaskAccessor;
+
+/**
+ * Registries for modifying villager interactions that
+ * villagers have with the world.
+ * @see VillagerPlantableRegistry for registering plants that farmers can plant
+ */
+public class VillagerInteractionRegistries {
+	private static final Logger LOGGER = LoggerFactory.getLogger(VillagerInteractionRegistries.class);
+
+	private VillagerInteractionRegistries() {
+	}
+
+	/**
+	 * Registers an item to be collectable (picked up from item entity)
+	 * by any profession villagers.
+	 *
+	 * @param item the item to register
+	 */
+	public static void registerCollectable(ItemConvertible item) {
+		Objects.requireNonNull(item.asItem(), "Item cannot be null!");
+		getCollectableRegistry().add(item.asItem());
+	}
+
+	/**
+	 * Registers an item to be use in a composter by farmer villagers.
+	 * @param item the item to register
+	 */
+	public static void registerCompostable(ItemConvertible item) {
+		Objects.requireNonNull(item.asItem(), "Item cannot be null!");
+		getCompostableRegistry().add(item.asItem());
+	}
+
+	/**
+	 * Registers an item to be edible by villagers.
+	 * @param item      the item to register
+	 * @param foodValue the amount of breeding power the item has (1 = normal food item, 4 = bread)
+	 */
+	public static void registerFood(ItemConvertible item, int foodValue) {
+		Objects.requireNonNull(item.asItem(), "Item cannot be null!");
+		Objects.requireNonNull(foodValue, "Food value cannot be null!");
+		Integer oldValue = getFoodRegistry().put(item.asItem(), foodValue);
+
+		if (oldValue != null) {
+			LOGGER.info("Overriding previous food value of {}, was: {}, now: {}", item.asItem().toString(), oldValue, foodValue);
+		}
+	}
+
+	/**
+	 * Registers a hero of the village gifts loot table to a profession.
+	 * @param profession the profession to modify
+	 * @param lootTable  the loot table to associate with the profession
+	 */
+	public static void registerGiftLootTable(VillagerProfession profession, Identifier lootTable) {
+		Objects.requireNonNull(profession, "Profession cannot be null!");
+		Objects.requireNonNull(lootTable, "Loot table identifier cannot be null!");
+		Identifier oldValue = GiveGiftsToHeroTaskAccessor.fabric_getGifts().put(profession, lootTable);
+
+		if (oldValue != null) {
+			LOGGER.info("Overriding previous gift loot table of {} profession, was: {}, now: {}", profession.getId(), oldValue, lootTable);
+		}
+	}
+
+	private static Set<Item> getCollectableRegistry() {
+		return ImmutableCollectionUtils.getAsMutableSet(VillagerEntityAccessor::fabric_getGatherableItems, VillagerEntityAccessor::fabric_setGatherableItems);
+	}
+
+	private static List<Item> getCompostableRegistry() {
+		return ImmutableCollectionUtils.getAsMutableList(FarmerWorkTaskAccessor::fabric_getCompostable, FarmerWorkTaskAccessor::fabric_setCompostables);
+	}
+
+	private static Map<Item, Integer> getFoodRegistry() {
+		return ImmutableCollectionUtils.getAsMutableMap(() -> VillagerEntity.ITEM_FOOD_VALUES, VillagerEntityAccessor::fabric_setItemFoodValues);
+	}
+}
diff --git a/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/VillagerPlantableRegistry.java b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/VillagerPlantableRegistry.java
new file mode 100644
index 000000000..6f8aa4ced
--- /dev/null
+++ b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/VillagerPlantableRegistry.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.api.registry;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Objects;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.minecraft.block.BlockState;
+import net.minecraft.block.CropBlock;
+import net.minecraft.item.BlockItem;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemConvertible;
+import net.minecraft.item.Items;
+import net.minecraft.util.registry.Registry;
+
+/**
+ * Registry of items that farmer villagers can plant on farmland.
+ */
+public class VillagerPlantableRegistry {
+	private static final Logger LOGGER = LoggerFactory.getLogger(VillagerPlantableRegistry.class);
+	private static final HashMap<Item, BlockState> PLANTABLES = new HashMap<>();
+	static {
+		register(Items.WHEAT_SEEDS);
+		register(Items.CARROT);
+		register(Items.POTATO);
+		register(Items.BEETROOT_SEEDS);
+	}
+
+	private VillagerPlantableRegistry() {
+	}
+
+	/**
+	 * Registers a BlockItem to be plantable by farmer villagers.
+	 * This will use the default state of the associated block.
+	 * For the crop to be harvestable, the block should extend CropBlock, so the
+	 * farmer can test the {@link CropBlock#isMature(BlockState)} method.
+	 * @param item the BlockItem to register
+	 */
+	public static void register(ItemConvertible item) {
+		Objects.requireNonNull(item.asItem(), "Item cannot be null!");
+
+		if (!(item.asItem() instanceof BlockItem)) {
+			throw new IllegalArgumentException("item is not a BlockItem");
+		}
+
+		register(item, ((BlockItem) item.asItem()).getBlock().getDefaultState());
+	}
+
+	/**
+	 * Register an item with an associated to be plantable by farmer villagers.
+	 * For the crop to be harvestable, the block should extend CropBlock, so the
+	 * farmer can test the {@link CropBlock#isMature(BlockState)} method.
+	 * @param item       the seed item
+	 * @param plantState the state that will be planted
+	 */
+	public static void register(ItemConvertible item, BlockState plantState) {
+		Objects.requireNonNull(item.asItem(), "Item cannot be null!");
+		Objects.requireNonNull(plantState, "Plant block state cannot be null!");
+
+		PLANTABLES.put(item.asItem(), plantState);
+
+		if (!(plantState.getBlock() instanceof CropBlock)) {
+			LOGGER.info("Registered a block ({}) that does not extend CropBlock, this block will not be villager harvestable by default.", Registry.BLOCK.getId(plantState.getBlock()));
+		}
+	}
+
+	/**
+	 * Tests if the item is a registered seed item.
+	 * @param item the item to test
+	 * @return true if the item is registered as a seed
+	 */
+	public static boolean contains(ItemConvertible item) {
+		Objects.requireNonNull(item.asItem(), "Item cannot be null!");
+		return PLANTABLES.containsKey(item.asItem());
+	}
+
+	/**
+	 * Get the state that is associated with the provided seed item.
+	 * @param item the seed item
+	 * @return the state associated with the seed item
+	 */
+	public static BlockState getPlantState(ItemConvertible item) {
+		Objects.requireNonNull(item.asItem(), "Item cannot be null!");
+		return PLANTABLES.get(item.asItem());
+	}
+
+	/**
+	 * Get all currently registered seed items.
+	 * @return all currently registered seed items.
+	 */
+	public static Set<Item> getItems() {
+		return Collections.unmodifiableSet(PLANTABLES.keySet());
+	}
+}
diff --git a/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/impl/content/registry/util/ImmutableCollectionUtils.java b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/impl/content/registry/util/ImmutableCollectionUtils.java
new file mode 100644
index 000000000..dd0c6207a
--- /dev/null
+++ b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/impl/content/registry/util/ImmutableCollectionUtils.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.impl.content.registry.util;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public class ImmutableCollectionUtils {
+	public static <T> Set<T> getAsMutableSet(Supplier<Set<T>> getter, Consumer<Set<T>> setter) {
+		Set<T> set = getter.get();
+
+		if (!(set instanceof HashSet)) {
+			setter.accept(set = new HashSet<>(set));
+		}
+
+		return set;
+	}
+
+	public static <T> List<T> getAsMutableList(Supplier<List<T>> getter, Consumer<List<T>> setter) {
+		List<T> set = getter.get();
+
+		if (!(set instanceof ArrayList)) {
+			setter.accept(set = new ArrayList<>(set));
+		}
+
+		return set;
+	}
+
+	public static <K, V> Map<K, V> getAsMutableMap(Supplier<Map<K, V>> getter, Consumer<Map<K, V>> setter) {
+		Map<K, V> map = getter.get();
+
+		if (!(map instanceof HashMap)) {
+			setter.accept(map = new HashMap<>(map));
+		}
+
+		return map;
+	}
+}
diff --git a/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/FarmerVillagerTaskMixin.java b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/FarmerVillagerTaskMixin.java
new file mode 100644
index 000000000..85f3dbb80
--- /dev/null
+++ b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/FarmerVillagerTaskMixin.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.content.registry;
+
+import org.jetbrains.annotations.Nullable;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.ModifyArg;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
+
+import net.minecraft.entity.ai.brain.task.FarmerVillagerTask;
+import net.minecraft.entity.passive.VillagerEntity;
+import net.minecraft.inventory.SimpleInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.server.world.ServerWorld;
+import net.minecraft.util.math.BlockPos;
+
+import net.fabricmc.fabric.api.registry.VillagerPlantableRegistry;
+
+@Mixin(FarmerVillagerTask.class)
+public class FarmerVillagerTaskMixin {
+	@Nullable
+	@Shadow private BlockPos currentTarget;
+
+	private int fabric_currentInventorySlot = -1;
+
+	@ModifyArg(method = "keepRunning", at = @At(value = "INVOKE", target = "Lnet/minecraft/inventory/SimpleInventory;getStack(I)Lnet/minecraft/item/ItemStack;"), index = 0)
+	private int fabric_storeCurrentSlot(int slot) {
+		return this.fabric_currentInventorySlot = slot;
+	}
+
+	@ModifyVariable(method = "keepRunning", at = @At("LOAD"))
+	private boolean fabric_useRegistryForPlace(boolean current, ServerWorld serverWorld, VillagerEntity villagerEntity, long l) {
+		if (current) {
+			return true;
+		}
+
+		SimpleInventory simpleInventory = villagerEntity.getInventory();
+		ItemStack currentStack = simpleInventory.getStack(this.fabric_currentInventorySlot);
+
+		if (!currentStack.isEmpty() && VillagerPlantableRegistry.contains(currentStack.getItem())) {
+			serverWorld.setBlockState(this.currentTarget, VillagerPlantableRegistry.getPlantState(currentStack.getItem()), 3);
+			return true;
+		}
+
+		return false;
+	}
+}
diff --git a/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/FarmerWorkTaskAccessor.java b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/FarmerWorkTaskAccessor.java
new file mode 100644
index 000000000..04e4c0115
--- /dev/null
+++ b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/FarmerWorkTaskAccessor.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.content.registry;
+
+import java.util.List;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Mutable;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+import net.minecraft.entity.ai.brain.task.FarmerWorkTask;
+import net.minecraft.item.Item;
+
+@Mixin(FarmerWorkTask.class)
+public interface FarmerWorkTaskAccessor {
+	@Mutable
+	@Accessor("COMPOSTABLES")
+	static void fabric_setCompostables(List<Item> items) {
+		throw new AssertionError("Untransformed @Accessor");
+	}
+
+	@Accessor("COMPOSTABLES")
+	static List<Item> fabric_getCompostable() {
+		throw new AssertionError("Untransformed @Accessor");
+	}
+}
diff --git a/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/GiveGiftsToHeroTaskAccessor.java b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/GiveGiftsToHeroTaskAccessor.java
new file mode 100644
index 000000000..86acc618d
--- /dev/null
+++ b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/GiveGiftsToHeroTaskAccessor.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.content.registry;
+
+import java.util.Map;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+import net.minecraft.entity.ai.brain.task.GiveGiftsToHeroTask;
+import net.minecraft.util.Identifier;
+import net.minecraft.village.VillagerProfession;
+
+@Mixin(GiveGiftsToHeroTask.class)
+public interface GiveGiftsToHeroTaskAccessor {
+	@Accessor("GIFTS")
+	static Map<VillagerProfession, Identifier> fabric_getGifts() {
+		throw new AssertionError("Untransformed @Accessor");
+	}
+}
diff --git a/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/VillagerEntityAccessor.java b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/VillagerEntityAccessor.java
new file mode 100644
index 000000000..5766ef523
--- /dev/null
+++ b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/VillagerEntityAccessor.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.content.registry;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Mutable;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+import net.minecraft.entity.passive.VillagerEntity;
+import net.minecraft.item.Item;
+
+@Mixin(VillagerEntity.class)
+public interface VillagerEntityAccessor {
+	@Mutable
+	@Accessor("ITEM_FOOD_VALUES")
+	static void fabric_setItemFoodValues(Map<Item, Integer> items) {
+		throw new AssertionError("Untransformed @Accessor");
+	}
+
+	@Mutable
+	@Accessor("GATHERABLE_ITEMS")
+	static void fabric_setGatherableItems(Set<Item> items) {
+		throw new AssertionError("Untransformed @Accessor");
+	}
+
+	@Accessor("GATHERABLE_ITEMS")
+	static Set<Item> fabric_getGatherableItems() {
+		throw new AssertionError("Untransformed @Accessor");
+	}
+}
diff --git a/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/VillagerEntityMixin.java b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/VillagerEntityMixin.java
new file mode 100644
index 000000000..eb24c3528
--- /dev/null
+++ b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/VillagerEntityMixin.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.content.registry;
+
+import java.util.Set;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Redirect;
+
+import net.minecraft.entity.passive.VillagerEntity;
+import net.minecraft.inventory.SimpleInventory;
+import net.minecraft.item.Item;
+
+import net.fabricmc.fabric.api.registry.VillagerPlantableRegistry;
+
+@Mixin(VillagerEntity.class)
+public class VillagerEntityMixin {
+	@Redirect(method = "hasSeedToPlant", at = @At(value = "INVOKE", target = "Lnet/minecraft/inventory/SimpleInventory;containsAny(Ljava/util/Set;)Z"))
+	private boolean fabric_useRegistry(SimpleInventory inventory, Set<Item> items) {
+		return inventory.containsAny(VillagerPlantableRegistry.getItems());
+	}
+}
diff --git a/fabric-content-registries-v0/src/main/resources/fabric-content-registries-v0.mixins.json b/fabric-content-registries-v0/src/main/resources/fabric-content-registries-v0.mixins.json
index 5c6ccd3c0..4bfadd6b1 100644
--- a/fabric-content-registries-v0/src/main/resources/fabric-content-registries-v0.mixins.json
+++ b/fabric-content-registries-v0/src/main/resources/fabric-content-registries-v0.mixins.json
@@ -4,12 +4,17 @@
   "compatibilityLevel": "JAVA_16",
   "mixins": [
     "AxeItemAccessor",
+    "FarmerVillagerTaskMixin",
+    "FarmerWorkTaskAccessor",
+    "GiveGiftsToHeroTaskAccessor",
     "HoeItemAccessor",
     "HoneycombItemMixin",
     "MixinAbstractFurnaceBlockEntity",
     "MixinFireBlock",
     "OxidizableMixin",
-    "ShovelItemAccessor"
+    "ShovelItemAccessor",
+    "VillagerEntityAccessor",
+    "VillagerEntityMixin"
   ],
   "injectors": {
     "defaultRequire": 1
diff --git a/fabric-content-registries-v0/src/testmod/java/net/fabricmc/fabric/test/content/registry/ContentRegistryTest.java b/fabric-content-registries-v0/src/testmod/java/net/fabricmc/fabric/test/content/registry/ContentRegistryTest.java
index d492963ee..f5a6b53ba 100644
--- a/fabric-content-registries-v0/src/testmod/java/net/fabricmc/fabric/test/content/registry/ContentRegistryTest.java
+++ b/fabric-content-registries-v0/src/testmod/java/net/fabricmc/fabric/test/content/registry/ContentRegistryTest.java
@@ -16,17 +16,22 @@
 
 package net.fabricmc.fabric.test.content.registry;
 
-import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import net.minecraft.block.Blocks;
 import net.minecraft.item.HoeItem;
+import net.minecraft.item.Items;
+import net.minecraft.util.Identifier;
+import net.minecraft.village.VillagerProfession;
 
 import net.fabricmc.api.ModInitializer;
 import net.fabricmc.fabric.api.registry.FlattenableBlockRegistry;
 import net.fabricmc.fabric.api.registry.OxidizableBlocksRegistry;
 import net.fabricmc.fabric.api.registry.StrippableBlockRegistry;
 import net.fabricmc.fabric.api.registry.TillableBlockRegistry;
+import net.fabricmc.fabric.api.registry.VillagerInteractionRegistries;
+import net.fabricmc.fabric.api.registry.VillagerPlantableRegistry;
 
 public final class ContentRegistryTest implements ModInitializer {
 	public static final Logger LOGGER = LoggerFactory.getLogger(ContentRegistryTest.class);
@@ -39,6 +44,9 @@ public final class ContentRegistryTest implements ModInitializer {
 		//  - green wool is tillable to lime wool
 		//  - copper ore, iron ore, gold ore, and diamond ore can be waxed into their deepslate variants and scraped back again
 		//  - aforementioned ores can be scraped from diamond -> gold -> iron -> copper
+		//  - villagers can now collect, consume (at the same level of bread) and compost apples
+		//  - villagers can now collect and plant oak saplings
+		//  - assign a loot table to the nitwit villager type
 
 		FlattenableBlockRegistry.register(Blocks.RED_WOOL, Blocks.YELLOW_WOOL.getDefaultState());
 		StrippableBlockRegistry.register(Blocks.QUARTZ_PILLAR, Blocks.HAY_BLOCK);
@@ -77,5 +85,24 @@ public final class ContentRegistryTest implements ModInitializer {
 			// expected behavior
 			LOGGER.info("OxidizableBlocksRegistry test passed!");
 		}
+
+		VillagerInteractionRegistries.registerCollectable(Items.APPLE);
+		VillagerInteractionRegistries.registerFood(Items.APPLE, 4);
+		VillagerInteractionRegistries.registerCompostable(Items.APPLE);
+
+		VillagerInteractionRegistries.registerCollectable(Items.OAK_SAPLING);
+		VillagerPlantableRegistry.register(Items.OAK_SAPLING);
+
+		// assert that VillagerPlantablesRegistry throws when getting a non-BlockItem
+		try {
+			VillagerPlantableRegistry.register(Items.STICK);
+
+			throw new AssertionError("VillagerPlantablesRegistry didn't throw when item is not BlockItem!");
+		} catch (Exception e) {
+			// expected behavior
+			LOGGER.info("VillagerPlantablesRegistry test passed!");
+		}
+
+		VillagerInteractionRegistries.registerGiftLootTable(VillagerProfession.NITWIT, new Identifier("fake_loot_table"));
 	}
 }