diff --git a/Content.Shared/Popups/SharedPopupSystem.cs b/Content.Shared/Popups/SharedPopupSystem.cs
index b57ed6659e..b2be7509f5 100644
--- a/Content.Shared/Popups/SharedPopupSystem.cs
+++ b/Content.Shared/Popups/SharedPopupSystem.cs
@@ -73,7 +73,7 @@ namespace Content.Shared.Popups
///
/// Variant of for use with prediction. The local client will
/// the popup to the recipient, and the server will show it to every other player in PVS range. If recipient is null, the local
- // client will do nothing and the server will show the message to every player in PVS range.
+ /// client will do nothing and the server will show the message to every player in PVS range.
///
public abstract void PopupPredictedCoordinates(string? message, EntityCoordinates coordinates, EntityUid? recipient, PopupType type = PopupType.Small);
diff --git a/Content.Shared/Trigger/Components/Effects/PopupOnTriggerComponent.cs b/Content.Shared/Trigger/Components/Effects/PopupOnTriggerComponent.cs
new file mode 100644
index 0000000000..0f85da81c3
--- /dev/null
+++ b/Content.Shared/Trigger/Components/Effects/PopupOnTriggerComponent.cs
@@ -0,0 +1,51 @@
+using Content.Shared.Popups;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Trigger.Components.Effects;
+
+///
+/// Displays a popup on the target when triggered.
+/// Will display the popup on the user when is true.
+///
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class PopupOnTriggerComponent : BaseXOnTriggerComponent
+{
+ ///
+ /// The text this popup will display to the recipient.
+ ///
+ [DataField(required: true), AutoNetworkedField]
+ public LocId Text;
+
+ ///
+ /// The text this popup will display to everything but the recipient.
+ /// If left null this will reuse .
+ ///
+ [DataField, AutoNetworkedField]
+ public LocId? OtherText;
+
+ ///
+ /// The size and color of the popup.
+ ///
+ [DataField, AutoNetworkedField]
+ public PopupType PopupType = PopupType.Small;
+
+ ///
+ /// If true, the user is the recipient of the popup.
+ /// If false, this entity is the recipient.
+ ///
+ [DataField, AutoNetworkedField]
+ public bool UserIsRecipient = true;
+
+ ///
+ /// If true, this popup will only play for the recipient and ignore prediction.
+ ///
+ [DataField, AutoNetworkedField]
+ public bool Quiet;
+
+ ///
+ /// Whether to use predicted popups.
+ ///
+ /// If false, this will spam any client that causes this trigger.
+ [DataField, AutoNetworkedField]
+ public bool Predicted = true;
+}
diff --git a/Content.Shared/Trigger/Systems/PopupOnTriggerSystem.cs b/Content.Shared/Trigger/Systems/PopupOnTriggerSystem.cs
new file mode 100644
index 0000000000..65ed216af1
--- /dev/null
+++ b/Content.Shared/Trigger/Systems/PopupOnTriggerSystem.cs
@@ -0,0 +1,62 @@
+using Content.Shared.Popups;
+using Content.Shared.Trigger.Components.Effects;
+
+namespace Content.Shared.Trigger.Systems;
+
+///
+/// This handles
+///
+public sealed class PopupOnTriggerSystem : EntitySystem
+{
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
+
+ ///
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnTrigger);
+ }
+
+ private void OnTrigger(Entity ent, ref TriggerEvent args)
+ {
+ if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key))
+ return;
+
+ var target = ent.Comp.TargetUser ? args.User : ent.Owner;
+
+ if (target == null)
+ return;
+
+ // Popups only play for one entity
+ if (ent.Comp.Quiet)
+ {
+ if (ent.Comp.Predicted)
+ _popup.PopupClient(Loc.GetString(ent.Comp.Text),
+ target.Value,
+ ent.Comp.UserIsRecipient ? args.User : ent.Owner,
+ ent.Comp.PopupType);
+
+ else if (args.User != null)
+ _popup.PopupEntity(Loc.GetString(ent.Comp.OtherText ?? ent.Comp.Text),
+ target.Value,
+ args.User.Value,
+ ent.Comp.PopupType);
+
+ return;
+ }
+
+ // Popups play for all entities
+ if (ent.Comp.Predicted)
+ _popup.PopupPredicted(Loc.GetString(ent.Comp.Text),
+ Loc.GetString(ent.Comp.OtherText ?? ent.Comp.Text),
+ target.Value,
+ ent.Comp.UserIsRecipient ? args.User : ent.Owner,
+ ent.Comp.PopupType);
+
+ else
+ _popup.PopupEntity(Loc.GetString(ent.Comp.OtherText ?? ent.Comp.Text),
+ target.Value,
+ ent.Comp.PopupType);
+ }
+}