diff --git a/fabric-content-registries-v0/build.gradle b/fabric-content-registries-v0/build.gradle
index 5f94f75df..4629bc4fe 100644
--- a/fabric-content-registries-v0/build.gradle
+++ b/fabric-content-registries-v0/build.gradle
@@ -1,5 +1,5 @@
 archivesBaseName = "fabric-content-registries-v0"
-version = getSubprojectVersion(project, "0.2.4")
+version = getSubprojectVersion(project, "0.3.0")
 
 loom {
 	accessWidener = file("src/main/resources/fabric-content-registries-v0.accesswidener")
diff --git a/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/FlattenableBlockRegistry.java b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/FlattenableBlockRegistry.java
new file mode 100644
index 000000000..99ffd555c
--- /dev/null
+++ b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/FlattenableBlockRegistry.java
@@ -0,0 +1,53 @@
+/*
+ * 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.Objects;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+
+import net.fabricmc.fabric.mixin.content.registry.ShovelItemAccessor;
+
+/**
+ * A registry for shovel flattening interactions. A vanilla example is turning dirt to dirt paths.
+ */
+public final class FlattenableBlockRegistry {
+	private static final Logger LOGGER = LogManager.getLogger();
+
+	private FlattenableBlockRegistry() {
+	}
+
+	/**
+	 * Registers a flattening interaction.
+	 *
+	 * @param input     the input block that can be flattened
+	 * @param flattened the flattened result block state
+	 */
+	public static void register(Block input, BlockState flattened) {
+		Objects.requireNonNull(input, "input block cannot be null");
+		Objects.requireNonNull(flattened, "flattened block state cannot be null");
+		BlockState old = ShovelItemAccessor.getPathStates().put(input, flattened);
+
+		if (old != null) {
+			LOGGER.debug("Replaced old flattening mapping from {} to {} with {}", input, old, flattened);
+		}
+	}
+}
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
new file mode 100644
index 000000000..3b70dbc93
--- /dev/null
+++ b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/StrippableBlockRegistry.java
@@ -0,0 +1,73 @@
+/*
+ * 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.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import net.minecraft.block.Block;
+import net.minecraft.state.property.Properties;
+
+import net.fabricmc.fabric.mixin.content.registry.AxeItemAccessor;
+
+/**
+ * A registry for axe stripping interactions. A vanilla example is turning logs to stripped logs.
+ */
+public final class StrippableBlockRegistry {
+	private static final Logger LOGGER = LogManager.getLogger();
+
+	private StrippableBlockRegistry() {
+	}
+
+	/**
+	 * Registers a stripping interaction.
+	 *
+	 * <p>Both blocks must have the {@link Properties#AXIS axis} property.
+	 *
+	 * @param input    the input block that can be stripped
+	 * @param stripped the stripped result block
+	 * @throws IllegalArgumentException if the input or the output doesn't have the {@link Properties#AXIS axis} property
+	 */
+	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);
+
+		if (old != null) {
+			LOGGER.debug("Replaced old stripping mapping from {} to {} with {}", input, old, stripped);
+		}
+	}
+
+	private static void requireNonNullAndAxisProperty(Block block, String name) {
+		Objects.requireNonNull(block, name + " cannot be null");
+
+		if (!block.getStateManager().getProperties().contains(Properties.AXIS)) {
+			throw new IllegalArgumentException(name + " must have the 'axis' property");
+		}
+	}
+}
diff --git a/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/TillableBlockRegistry.java b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/TillableBlockRegistry.java
new file mode 100644
index 000000000..037205e9b
--- /dev/null
+++ b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/api/registry/TillableBlockRegistry.java
@@ -0,0 +1,86 @@
+/*
+ * 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.Objects;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+import com.mojang.datafixers.util.Pair;
+
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.item.HoeItem;
+import net.minecraft.item.ItemConvertible;
+import net.minecraft.item.ItemUsageContext;
+
+import net.fabricmc.fabric.mixin.content.registry.HoeItemAccessor;
+
+/**
+ * A registry for hoe tilling interactions. A vanilla example is turning dirt to dirt paths.
+ */
+public final class TillableBlockRegistry {
+	private TillableBlockRegistry() {
+	}
+
+	/**
+	 * Registers a tilling interaction.
+	 *
+	 * <p>Tilling interactions are a two-step process. First, a usage predicate is run that decides whether to till
+	 * a block. If the predicate returns {@code true}, an action is executed. Default instances of these can be created
+	 * with these {@link HoeItem} methods:
+	 * <ul>
+	 * <li>usage predicate for farmland-like behavior: {@link HoeItem#usagePredicate(ItemUsageContext)}</li>
+	 * <li>simple action: {@link HoeItem#getTillingConsumer(BlockState)}</li>
+	 * <li>simple action that also drops an item: {@link HoeItem#getTillingConsumer(BlockState, ItemConvertible)}</li>
+	 * </ul>
+	 *
+	 * @param input          the input block that can be tilled
+	 * @param usagePredicate a predicate that filters if the block can be tilled
+	 * @param tillingAction  an action that is executed if the predicate returns {@code true}
+	 */
+	public static void register(Block input, Predicate<ItemUsageContext> usagePredicate, Consumer<ItemUsageContext> tillingAction) {
+		Objects.requireNonNull(input, "input block cannot be null");
+		HoeItemAccessor.getTilledBlocks().put(input, Pair.of(usagePredicate, tillingAction));
+	}
+
+	/**
+	 * Registers a simple tilling interaction.
+	 *
+	 * @param input          the input block that can be tilled
+	 * @param usagePredicate a predicate that filters if the block can be tilled
+	 * @param tilled         the tilled result block state
+	 */
+	public static void register(Block input, Predicate<ItemUsageContext> usagePredicate, BlockState tilled) {
+		Objects.requireNonNull(tilled, "tilled block state cannot be null");
+		register(input, usagePredicate, HoeItem.getTillingConsumer(tilled));
+	}
+
+	/**
+	 * Registers a simple tilling interaction that also drops an item.
+	 *
+	 * @param input          the input block that can be tilled
+	 * @param usagePredicate a predicate that filters if the block can be tilled
+	 * @param tilled         the tilled result block state
+	 * @param droppedItem    an item that is dropped when the input block is tilled
+	 */
+	public static void register(Block input, Predicate<ItemUsageContext> usagePredicate, BlockState tilled, ItemConvertible droppedItem) {
+		Objects.requireNonNull(tilled, "tilled block state cannot be null");
+		Objects.requireNonNull(droppedItem, "dropped item cannot be null");
+		register(input, usagePredicate, HoeItem.getTillingConsumer(tilled, droppedItem));
+	}
+}
diff --git a/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/AxeItemAccessor.java b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/AxeItemAccessor.java
new file mode 100644
index 000000000..8e61926bc
--- /dev/null
+++ b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/AxeItemAccessor.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.Map;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Mutable;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.AxeItem;
+
+@Mixin(AxeItem.class)
+public interface AxeItemAccessor {
+	@Accessor("STRIPPED_BLOCKS")
+	static Map<Block, Block> getStrippedBlocks() {
+		throw new AssertionError("Untransformed @Accessor");
+	}
+
+	@Accessor("STRIPPED_BLOCKS")
+	@Mutable
+	static void setStrippedBlocks(Map<Block, Block> strippedBlocks) {
+		throw new AssertionError("Untransformed @Accessor");
+	}
+}
diff --git a/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/HoeItemAccessor.java b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/HoeItemAccessor.java
new file mode 100644
index 000000000..9d35665f6
--- /dev/null
+++ b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/HoeItemAccessor.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.Map;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+import com.mojang.datafixers.util.Pair;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.HoeItem;
+import net.minecraft.item.ItemUsageContext;
+
+@Mixin(HoeItem.class)
+public interface HoeItemAccessor {
+	@Accessor("TILLED_BLOCKS")
+	static Map<Block, Pair<Predicate<ItemUsageContext>, Consumer<ItemUsageContext>>> getTilledBlocks() {
+		throw new AssertionError("Untransformed @Accessor");
+	}
+}
diff --git a/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/ShovelItemAccessor.java b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/ShovelItemAccessor.java
new file mode 100644
index 000000000..79b73f918
--- /dev/null
+++ b/fabric-content-registries-v0/src/main/java/net/fabricmc/fabric/mixin/content/registry/ShovelItemAccessor.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.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.item.ShovelItem;
+
+@Mixin(ShovelItem.class)
+public interface ShovelItemAccessor {
+	@Accessor("PATH_STATES")
+	static Map<Block, BlockState> getPathStates() {
+		throw new AssertionError("Untransformed @Accessor");
+	}
+}
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 fe3232675..f1a87ae74 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
@@ -3,8 +3,11 @@
   "package": "net.fabricmc.fabric.mixin.content.registry",
   "compatibilityLevel": "JAVA_16",
   "mixins": [
+    "AxeItemAccessor",
+    "HoeItemAccessor",
     "MixinAbstractFurnaceBlockEntity",
-    "MixinFireBlock"
+    "MixinFireBlock",
+    "ShovelItemAccessor"
   ],
   "client": [
     "client.MixinClientPlayNetworkHandler"
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
new file mode 100644
index 000000000..7ea7be8d2
--- /dev/null
+++ b/fabric-content-registries-v0/src/testmod/java/net/fabricmc/fabric/test/content/registry/ContentRegistryTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.test.content.registry;
+
+import net.minecraft.block.Blocks;
+import net.minecraft.item.HoeItem;
+
+import net.fabricmc.api.ModInitializer;
+import net.fabricmc.fabric.api.registry.FlattenableBlockRegistry;
+import net.fabricmc.fabric.api.registry.StrippableBlockRegistry;
+import net.fabricmc.fabric.api.registry.TillableBlockRegistry;
+
+public final class ContentRegistryTest implements ModInitializer {
+	@Override
+	public void onInitialize() {
+		// Expected behavior:
+		//  - red wool is flattenable to yellow wool
+		//  - quartz pillars are strippable to hay blocks
+		//  - green wool is tillable to lime wool
+
+		FlattenableBlockRegistry.register(Blocks.RED_WOOL, Blocks.YELLOW_WOOL.getDefaultState());
+		StrippableBlockRegistry.register(Blocks.QUARTZ_PILLAR, Blocks.HAY_BLOCK);
+
+		// assert that StrippableBlockRegistry throws when the blocks don't have 'axis'
+		try {
+			StrippableBlockRegistry.register(Blocks.BLUE_WOOL, Blocks.OAK_LOG);
+			StrippableBlockRegistry.register(Blocks.HAY_BLOCK, Blocks.BLUE_WOOL);
+			throw new AssertionError("StrippableBlockRegistry didn't throw when blocks where missing the 'axis' property!");
+		} catch (IllegalArgumentException e) {
+			// expected behavior
+		}
+
+		TillableBlockRegistry.register(Blocks.GREEN_WOOL, context -> true, HoeItem.getTillingConsumer(Blocks.LIME_WOOL.getDefaultState()));
+	}
+}
diff --git a/fabric-content-registries-v0/src/testmod/resources/fabric.mod.json b/fabric-content-registries-v0/src/testmod/resources/fabric.mod.json
new file mode 100644
index 000000000..a0e697e2f
--- /dev/null
+++ b/fabric-content-registries-v0/src/testmod/resources/fabric.mod.json
@@ -0,0 +1,16 @@
+{
+  "schemaVersion": 1,
+  "id": "fabric-content-registries-v0-testmod",
+  "name": "Fabric Content Registries (v0) Test Mod",
+  "version": "1.0.0",
+  "environment": "*",
+  "license": "Apache-2.0",
+  "depends": {
+    "fabric-content-registries-v0": "*"
+  },
+  "entrypoints": {
+    "main": [
+      "net.fabricmc.fabric.test.content.registry.ContentRegistryTest"
+    ]
+  }
+}