diff --git a/esoui/ingame/lfg/preferredroles.lua b/esoui/ingame/lfg/preferredroles.lua
new file mode 100644
index 0000000..85b9f5a
--- /dev/null
+++ b/esoui/ingame/lfg/preferredroles.lua
@@ -0,0 +1,151 @@
+--------------------------------------
+--Preferred Roles Manager
+--------------------------------------
+local PreferredRolesManager = ZO_Object:Subclass()
+
+function PreferredRolesManager:New(...)
+    local manager = ZO_Object.New(self)
+    manager:Initialize(...)
+    return manager
+end
+
+function PreferredRolesManager:Initialize(control)
+    self.control = control
+    self:InitializeRoles()
+
+    local function OnGroupingToolsStatusUpdate(isSearching)
+        self:DisableRoleButtons(isSearching)
+    end
+
+    local function OnPlayerActivated()
+        OnGroupingToolsStatusUpdate(IsCurrentlySearchingForGroup())
+    end
+
+    self.control:RegisterForEvent(EVENT_GROUPING_TOOLS_STATUS_UPDATE, function(event, ...) OnGroupingToolsStatusUpdate(...) end)
+    self.control:RegisterForEvent(EVENT_PLAYER_ACTIVATED, OnPlayerActivated)
+end
+
+function PreferredRolesManager:InitializeRoles()
+    local isDPS, isHeal, isTank = GetPlayerRoles()
+    self.roles = {
+        [LFG_ROLE_DPS] = {
+            button = self.control:GetNamedChild("ButtonsDPS"),
+            isSelected = isDPS,
+            tooltip = GetString(SI_GROUP_PREFERRED_ROLE_DPS_TOOLTIP),
+        },
+        [LFG_ROLE_HEAL] = {
+            button = self.control:GetNamedChild("ButtonsHeal"),
+            isSelected = isHeal,
+            tooltip = GetString(SI_GROUP_PREFERRED_ROLE_HEAL_TOOLTIP),
+        },
+        [LFG_ROLE_TANK] = {
+            button = self.control:GetNamedChild("ButtonsTank"),
+            isSelected = isTank,
+            tooltip = GetString(SI_GROUP_PREFERRED_ROLE_TANK_TOOLTIP),
+        },
+    }
+
+    for roleType, roleData in pairs(self.roles) do
+        if roleData.isSelected then
+            ZO_CheckButton_SetChecked(roleData.button)
+        end
+    end
+end
+
+function PreferredRolesManager:SetRoleToggled(role, selected)
+    self.roles[role].isSelected = selected
+    PlaySound(selected and SOUNDS.GROUP_ROLE_SELECTED or SOUNDS.GROUP_ROLE_DESELECTED)
+    UpdatePlayerRole(role, selected)
+    ZO_ACTIVITY_FINDER_ROOT_MANAGER:UpdateLocationData()
+end
+
+function PreferredRolesManager:DisableRoleButtons(isDisabled)
+    for roleType, roleData in pairs(self.roles) do
+
+        --Force buttons only half selected (mouse down only) to be unselected before disabling
+        if not roleData.isSelected then
+            ZO_CheckButton_SetUnchecked(roleData.button, false)
+        end
+
+        ZO_CheckButton_SetEnableState(roleData.button, not isDisabled)
+
+        --Force mouse to be enabled on disabled buttons so tooltips still work
+        roleData.button:SetMouseEnabled(true)
+    end
+end
+
+function PreferredRolesManager:GetSelectedRoleCount()
+    local count = 0
+    for roleType, roleData in pairs(self.roles) do
+        if roleData.isSelected then
+            count = count + 1
+        end
+    end
+
+    return count
+end
+
+function PreferredRolesManager:GetRoles()
+    local roles = self.roles
+    return {
+        [LFG_ROLE_DPS] = roles[LFG_ROLE_DPS].isSelected,
+        [LFG_ROLE_HEAL] = roles[LFG_ROLE_HEAL].isSelected,
+        [LFG_ROLE_TANK] = roles[LFG_ROLE_TANK].isSelected,
+    }
+end
+
+---- XML Callbacks ----
+
+function ZO_PreferredRolesButton_OnMouseEnter(control)
+    InitializeTooltip(InformationTooltip, control, BOTTOM, 0, 0)
+    local r, g, b = ZO_TOOLTIP_DEFAULT_COLOR:UnpackRGB()
+    InformationTooltip:AddLine(GetString("SI_LFGROLE", control.role), "", r, g, b)
+    InformationTooltip:AddLine(PREFERRED_ROLES.roles[control.role].tooltip, "", r, g, b)
+    InformationTooltip:AddLine(GetString(SI_GROUP_PREFERRED_ROLE_DESCRIPTION), "", r, g, b)
+    local currentState = control:GetState()
+    if currentState == BSTATE_DISABLED or currentState == BSTATE_DISABLED_PRESSED then
+        InformationTooltip:AddLine(zo_strformat(SI_GROUP_LIST_PANEL_DISABLED_ROLE_TOOLTIP, tooltipText), "", ZO_ColorDef:New("ff0000"):UnpackRGB())
+    end
+end
+
+function ZO_PreferredRolesButton_OnMouseExit(control)
+    ClearTooltip(InformationTooltip)
+end
+
+function ZO_PreferredRolesButton_OnClicked(buttonControl, mouseButton)
+    local buttonState = buttonControl:GetState()
+    local role = buttonControl.role
+
+    if buttonState == BSTATE_NORMAL then
+        ZO_CheckButton_SetChecked(buttonControl)
+        PREFERRED_ROLES:SetRoleToggled(role, true)
+
+    elseif buttonState == BSTATE_PRESSED and PREFERRED_ROLES:GetSelectedRoleCount() > 1 then --enforce having at least one role selected
+        ZO_CheckButton_SetUnchecked(buttonControl)
+        PREFERRED_ROLES:SetRoleToggled(role, false)
+    end
+end
+
+do
+    local ROLE_NAME_LOOKUP =
+    {
+        [LFG_ROLE_TANK] = "tank",
+        [LFG_ROLE_HEAL] = "healer",
+        [LFG_ROLE_DPS] = "dps",
+    }
+
+    function ZO_PreferredRoleButton_OnInitialized(control, role)
+        local roleName = ROLE_NAME_LOOKUP[role]
+        control:SetNormalTexture(string.format("EsoUI/Art/LFG/LFG_%s_up_64.dds", roleName))
+        control:SetPressedTexture(string.format("EsoUI/Art/LFG/LFG_%s_down_64.dds", roleName))
+        control:SetMouseOverTexture(string.format("EsoUI/Art/LFG/LFG_%s_over_64.dds", roleName))
+        control:SetPressedMouseOverTexture(string.format("EsoUI/Art/LFG/LFG_%s_down_over_64.dds", roleName))
+        control:SetDisabledTexture(string.format("EsoUI/Art/LFG/LFG_%s_disabled_64.dds", roleName))
+        control:SetDisabledPressedTexture(string.format("EsoUI/Art/LFG/LFG_%s_down_disabled_64.dds", roleName))
+        control.role = role
+    end
+end
+
+function ZO_PreferredRoles_OnInitialized(self)
+    PREFERRED_ROLES = PreferredRolesManager:New(self)
+end
\ No newline at end of file