diff --git a/Content.Shared/Follower/Components/FollowedComponent.cs b/Content.Shared/Follower/Components/FollowedComponent.cs
new file mode 100644
index 0000000000..013af41302
--- /dev/null
+++ b/Content.Shared/Follower/Components/FollowedComponent.cs
@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+using Robust.Shared.Analyzers;
+using Robust.Shared.GameObjects;
+
+namespace Content.Shared.Follower.Components;
+
+///
+/// Attached to entities that are currently being followed by a ghost.
+///
+[RegisterComponent, ComponentProtoName("Followed"), Friend(typeof(FollowerSystem))]
+public class FollowedComponent : Component
+{
+ public HashSet Following = new();
+}
diff --git a/Content.Shared/Follower/Components/FollowerComponent.cs b/Content.Shared/Follower/Components/FollowerComponent.cs
new file mode 100644
index 0000000000..c1f204d2a4
--- /dev/null
+++ b/Content.Shared/Follower/Components/FollowerComponent.cs
@@ -0,0 +1,11 @@
+using Robust.Shared.Analyzers;
+using Robust.Shared.GameObjects;
+
+namespace Content.Shared.Follower.Components;
+
+[RegisterComponent, ComponentProtoName("Follower")]
+[Friend(typeof(FollowerSystem))]
+public class FollowerComponent : Component
+{
+ public EntityUid Following;
+}
diff --git a/Content.Shared/Follower/FollowerSystem.cs b/Content.Shared/Follower/FollowerSystem.cs
new file mode 100644
index 0000000000..d99d02f47a
--- /dev/null
+++ b/Content.Shared/Follower/FollowerSystem.cs
@@ -0,0 +1,106 @@
+using Content.Shared.Database;
+using Content.Shared.Ghost;
+using Content.Shared.Movement.EntitySystems;
+using Content.Shared.Verbs;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Localization;
+using Content.Shared.Follower.Components;
+using Robust.Shared.Maths;
+
+namespace Content.Shared.Follower;
+
+public class FollowerSystem : EntitySystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnGetAlternativeVerbs);
+ SubscribeLocalEvent(OnFollowerMove);
+ SubscribeLocalEvent(OnFollowedTerminating);
+ }
+
+ private void OnGetAlternativeVerbs(GetAlternativeVerbsEvent ev)
+ {
+ if (!HasComp(ev.User))
+ return;
+
+ var verb = new Verb
+ {
+ Priority = 10,
+ Act = (() =>
+ {
+ StartFollowingEntity(ev.User, ev.Target);
+ }),
+ Impact = LogImpact.Low,
+ Text = Loc.GetString("verb-follow-text"),
+ IconTexture = "/Textures/Interface/VerbIcons/open.svg.192dpi.png",
+ };
+
+ ev.Verbs.Add(verb);
+ }
+
+ private void OnFollowerMove(EntityUid uid, FollowerComponent component, RelayMoveInputEvent args)
+ {
+ StopFollowingEntity(uid, component.Following);
+ }
+
+ // Since we parent our observer to the followed entity, we need to detach
+ // before they get deleted so that we don't get recursively deleted too.
+ private void OnFollowedTerminating(EntityUid uid, FollowedComponent component, EntityTerminatingEvent args)
+ {
+ StopAllFollowers(uid, component);
+ }
+
+ ///
+ /// Makes an entity follow another entity, by parenting to it.
+ ///
+ /// The entity that should follow
+ /// The entity to be followed
+ public void StartFollowingEntity(EntityUid follower, EntityUid entity)
+ {
+ var followerComp = EnsureComp(follower);
+ followerComp.Following = entity;
+
+ var followedComp = EnsureComp(entity);
+ followedComp.Following.Add(follower);
+
+ var xform = Transform(follower);
+ xform.AttachParent(entity);
+ xform.LocalPosition = Vector2.Zero;
+ }
+
+ ///
+ /// Forces an entity to stop following another entity, if it is doing so.
+ ///
+ public void StopFollowingEntity(EntityUid uid, EntityUid target,
+ FollowedComponent? followed=null)
+ {
+ if (!Resolve(target, ref followed))
+ return;
+
+ if (!HasComp(uid))
+ return;
+
+ followed.Following.Remove(uid);
+ if (followed.Following.Count == 0)
+ RemComp(target);
+ RemComp(uid);
+ Transform(uid).AttachToGridOrMap();
+ }
+
+ ///
+ /// Forces all of an entity's followers to stop following it.
+ ///
+ public void StopAllFollowers(EntityUid uid,
+ FollowedComponent? followed=null)
+ {
+ if (!Resolve(uid, ref followed))
+ return;
+
+ foreach (var player in followed.Following)
+ {
+ StopFollowingEntity(player, uid, followed);
+ }
+ }
+}
diff --git a/Resources/Locale/en-US/follower/follow-verb.ftl b/Resources/Locale/en-US/follower/follow-verb.ftl
new file mode 100644
index 0000000000..dbcac9d352
--- /dev/null
+++ b/Resources/Locale/en-US/follower/follow-verb.ftl
@@ -0,0 +1 @@
+verb-follow-text = Follow