diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java
index fbd5e6e00..547cb644c 100644
--- a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java
+++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java
@@ -16,7 +16,13 @@
 
 package net.fabricmc.fabric.api.item.v1;
 
+import com.google.common.collect.Multimap;
+
+import net.minecraft.block.BlockState;
 import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.entity.EquipmentSlot;
+import net.minecraft.entity.attribute.EntityAttribute;
+import net.minecraft.entity.attribute.EntityAttributeModifier;
 import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
@@ -25,9 +31,7 @@ import net.minecraft.util.Hand;
 /**
  * General-purpose Fabric-provided extensions for {@link Item} subclasses.
  *
- * <p>Note: This interface is automatically implemented on all items via Mixin,
- * however it has to be implemented explicitly by modders to be able to override functions.
- * In the future, it is planned that {@code public class Item implements FabricItem} will be visible in a development environment.
+ * <p>Note: This interface is automatically implemented on all items via Mixin and interface injection.
  *
  * <p>Note to maintainers: Functions should only be added to this interface if they are general-purpose enough,
  * to be evaluated on a case-by-case basis. Otherwise they are better suited for more specialized APIs.
@@ -61,4 +65,30 @@ public interface FabricItem {
 	default boolean allowContinuingBlockBreaking(PlayerEntity player, ItemStack oldStack, ItemStack newStack) {
 		return false;
 	}
+
+	/**
+	 * Return the attribute modifiers to apply when this stack is worn in a living entity equipment slot.
+	 * Stack-aware version of {@link Item#getAttributeModifiers(EquipmentSlot)}.
+	 *
+	 * <p>Note that attribute modifiers are only updated when the stack changes, i.e. when {@code ItemStack.areEqual(old, new)} is false.
+	 *
+	 * @param stack the current stack
+	 * @param slot  the equipment slot this stack is in
+	 * @return the attribute modifiers
+	 */
+	default Multimap<EntityAttribute, EntityAttributeModifier> getAttributeModifiers(ItemStack stack, EquipmentSlot slot) {
+		return ((Item) this).getAttributeModifiers(slot);
+	}
+
+	/**
+	 * Determines if mining with this item allows drops to be harvested from the specified block state.
+	 * Stack-aware version of {@link Item#isSuitableFor(BlockState)}.
+	 *
+	 * @param stack the current stack
+	 * @param state the block state of the targeted block
+	 * @return true if drops can be harvested
+	 */
+	default boolean isSuitableFor(ItemStack stack, BlockState state) {
+		return ((Item) this).isSuitableFor(state);
+	}
 }
diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemStackMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemStackMixin.java
index cd38ef4f8..e0b559dc4 100644
--- a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemStackMixin.java
+++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemStackMixin.java
@@ -18,15 +18,21 @@ package net.fabricmc.fabric.mixin.item;
 
 import java.util.function.Consumer;
 
+import com.google.common.collect.Multimap;
 import org.spongepowered.asm.mixin.Mixin;
 import org.spongepowered.asm.mixin.Shadow;
 import org.spongepowered.asm.mixin.Unique;
 import org.spongepowered.asm.mixin.injection.At;
 import org.spongepowered.asm.mixin.injection.Inject;
 import org.spongepowered.asm.mixin.injection.ModifyArg;
+import org.spongepowered.asm.mixin.injection.Redirect;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
 
+import net.minecraft.block.BlockState;
+import net.minecraft.entity.EquipmentSlot;
 import net.minecraft.entity.LivingEntity;
+import net.minecraft.entity.attribute.EntityAttribute;
+import net.minecraft.entity.attribute.EntityAttributeModifier;
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
 
@@ -65,4 +71,26 @@ public abstract class ItemStackMixin {
 		this.fabric_damagingEntity = null;
 		this.fabric_breakCallback = null;
 	}
+
+	@Redirect(
+			method = "getAttributeModifiers",
+			at = @At(
+					value = "INVOKE",
+					target = "Lnet/minecraft/item/Item;getAttributeModifiers(Lnet/minecraft/entity/EquipmentSlot;)Lcom/google/common/collect/Multimap;"
+			)
+	)
+	public Multimap<EntityAttribute, EntityAttributeModifier> hookGetAttributeModifiers(Item item, EquipmentSlot slot) {
+		return item.getAttributeModifiers((ItemStack) (Object) this, slot);
+	}
+
+	@Redirect(
+			method = "isSuitableFor",
+			at = @At(
+					value = "INVOKE",
+					target = "Lnet/minecraft/item/Item;isSuitableFor(Lnet/minecraft/block/BlockState;)Z"
+			)
+	)
+	public boolean hookIsSuitableFor(Item item, BlockState state) {
+		return item.isSuitableFor((ItemStack) (Object) this, state);
+	}
 }
diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/client/ClientPlayerInteractionManagerMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/client/ClientPlayerInteractionManagerMixin.java
index b16e05bcf..61f2de56f 100644
--- a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/client/ClientPlayerInteractionManagerMixin.java
+++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/client/ClientPlayerInteractionManagerMixin.java
@@ -27,8 +27,6 @@ import net.minecraft.client.network.ClientPlayerInteractionManager;
 import net.minecraft.item.ItemStack;
 import net.minecraft.util.math.BlockPos;
 
-import net.fabricmc.fabric.api.item.v1.FabricItem;
-
 @Mixin(ClientPlayerInteractionManager.class)
 public class ClientPlayerInteractionManagerMixin {
 	@Shadow
@@ -58,7 +56,7 @@ public class ClientPlayerInteractionManagerMixin {
 			ItemStack oldStack = this.selectedStack;
 			ItemStack newStack = this.client.player.getMainHandStack();
 
-			if (oldStack.isOf(newStack.getItem()) && ((FabricItem) oldStack.getItem()).allowContinuingBlockBreaking(this.client.player, oldStack, newStack)) {
+			if (oldStack.isOf(newStack.getItem()) && oldStack.getItem().allowContinuingBlockBreaking(this.client.player, oldStack, newStack)) {
 				stackUnchanged = true;
 			}
 		}
diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/client/HeldItemRendererMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/client/HeldItemRendererMixin.java
index 55416a36f..3f10a0e21 100644
--- a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/client/HeldItemRendererMixin.java
+++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/client/HeldItemRendererMixin.java
@@ -51,7 +51,7 @@ public class HeldItemRendererMixin {
 		ItemStack newMainStack = client.player.getMainHandStack();
 
 		if (mainHand.getItem() == newMainStack.getItem()) {
-			if (!((FabricItem) mainHand.getItem()).allowNbtUpdateAnimation(client.player, Hand.MAIN_HAND, mainHand, newMainStack)) {
+			if (!mainHand.getItem().allowNbtUpdateAnimation(client.player, Hand.MAIN_HAND, mainHand, newMainStack)) {
 				mainHand = newMainStack;
 			}
 		}
@@ -60,7 +60,7 @@ public class HeldItemRendererMixin {
 		ItemStack newOffStack = client.player.getOffHandStack();
 
 		if (offHand.getItem() == newOffStack.getItem()) {
-			if (!((FabricItem) offHand.getItem()).allowNbtUpdateAnimation(client.player, Hand.OFF_HAND, offHand, newOffStack)) {
+			if (!offHand.getItem().allowNbtUpdateAnimation(client.player, Hand.OFF_HAND, offHand, newOffStack)) {
 				offHand = newOffStack;
 			}
 		}
diff --git a/fabric-item-api-v1/src/main/resources/fabric.mod.json b/fabric-item-api-v1/src/main/resources/fabric.mod.json
index 6a2e17f7c..7798a4d0d 100644
--- a/fabric-item-api-v1/src/main/resources/fabric.mod.json
+++ b/fabric-item-api-v1/src/main/resources/fabric.mod.json
@@ -24,6 +24,9 @@
   },
   "description": "Hooks for items",
   "custom": {
-    "fabric-api:module-lifecycle": "stable"
+    "fabric-api:module-lifecycle": "stable",
+    "loom:injected_interfaces": {
+      "net/minecraft/class_1792": ["net/fabricmc/fabric/api/item/v1/FabricItem"]
+    }
   }
 }
diff --git a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/UpdatingItem.java b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/UpdatingItem.java
index b994fe962..44c295c02 100644
--- a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/UpdatingItem.java
+++ b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/UpdatingItem.java
@@ -16,7 +16,15 @@
 
 package net.fabricmc.fabric.test.item;
 
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+
+import net.minecraft.block.BlockState;
 import net.minecraft.entity.Entity;
+import net.minecraft.entity.EquipmentSlot;
+import net.minecraft.entity.attribute.EntityAttribute;
+import net.minecraft.entity.attribute.EntityAttributeModifier;
+import net.minecraft.entity.attribute.EntityAttributes;
 import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemGroup;
@@ -25,9 +33,10 @@ import net.minecraft.nbt.NbtCompound;
 import net.minecraft.util.Hand;
 import net.minecraft.world.World;
 
-import net.fabricmc.fabric.api.item.v1.FabricItem;
+public class UpdatingItem extends Item {
+	private static final EntityAttributeModifier PLUS_FIVE = new EntityAttributeModifier(
+			ATTACK_DAMAGE_MODIFIER_ID, "updating item", 5, EntityAttributeModifier.Operation.ADDITION);
 
-public class UpdatingItem extends Item implements FabricItem {
 	private final boolean allowUpdateAnimation;
 
 	public UpdatingItem(boolean allowUpdateAnimation) {
@@ -52,4 +61,30 @@ public class UpdatingItem extends Item implements FabricItem {
 	public boolean allowContinuingBlockBreaking(PlayerEntity player, ItemStack oldStack, ItemStack newStack) {
 		return true; // set to false and you won't be able to break a block in survival with this item
 	}
+
+	// True for 15 seconds every 30 seconds
+	private boolean isEnabled(ItemStack stack) {
+		return !stack.hasNbt() || stack.getNbt().getLong("ticks") % 600 < 300;
+	}
+
+	@Override
+	public Multimap<EntityAttribute, EntityAttributeModifier> getAttributeModifiers(ItemStack stack, EquipmentSlot slot) {
+		// Give + 5 attack damage for 15 seconds every 30 seconds.
+		if (slot == EquipmentSlot.MAINHAND && isEnabled(stack)) {
+			return ImmutableMultimap.of(EntityAttributes.GENERIC_ATTACK_DAMAGE, PLUS_FIVE);
+		} else {
+			return ImmutableMultimap.of();
+		}
+	}
+
+	@Override
+	public boolean isSuitableFor(ItemStack stack, BlockState state) {
+		// Suitable for everything for 15 seconds every 30 seconds.
+		return isEnabled(stack);
+	}
+
+	@Override
+	public float getMiningSpeedMultiplier(ItemStack stack, BlockState state) {
+		return isEnabled(stack) ? 20 : super.getMiningSpeedMultiplier(stack, state);
+	}
 }