diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs
index 0df6605cdd..f24589ed35 100644
--- a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs
+++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs
@@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Menus
         {
             typeof(ToolbarButton),
             typeof(ToolbarRulesetSelector),
-            typeof(ToolbarRulesetButton),
+            typeof(ToolbarRulesetTabButton),
             typeof(ToolbarNotificationButton),
         };
 
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs
new file mode 100644
index 0000000000..582303024b
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs
@@ -0,0 +1,42 @@
+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics.Containers;
+using osu.Game.Overlays.Toolbar;
+using System;
+using System.Collections.Generic;
+using osu.Framework.Graphics;
+using System.Linq;
+using osu.Framework.MathUtils;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+    public class TestSceneToolbarRulesetSelector : OsuTestScene
+    {
+        public override IReadOnlyList<Type> RequiredTypes => new[]
+        {
+            typeof(ToolbarRulesetSelector),
+            typeof(ToolbarRulesetTabButton),
+        };
+
+        public TestSceneToolbarRulesetSelector()
+        {
+            ToolbarRulesetSelector selector;
+
+            Add(new Container
+            {
+                Anchor = Anchor.Centre,
+                Origin = Anchor.Centre,
+                AutoSizeAxes = Axes.X,
+                Height = Toolbar.HEIGHT,
+                Child = selector = new ToolbarRulesetSelector()
+            });
+
+            AddStep("Select random", () =>
+            {
+                selector.Current.Value = selector.Items.ElementAt(RNG.Next(selector.Items.Count()));
+            });
+            AddStep("Toggle disabled state", () => selector.Current.Disabled = !selector.Current.Disabled);
+        }
+    }
+}
diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs
index 982fb26b6b..19038c3981 100644
--- a/osu.Game/Overlays/Toolbar/Toolbar.cs
+++ b/osu.Game/Overlays/Toolbar/Toolbar.cs
@@ -12,6 +12,7 @@ using osu.Framework.Graphics.Shapes;
 using osu.Framework.Allocation;
 using osu.Framework.Bindables;
 using osu.Framework.Input.Events;
+using osu.Game.Rulesets;
 
 namespace osu.Game.Overlays.Toolbar
 {
@@ -23,6 +24,7 @@ namespace osu.Game.Overlays.Toolbar
         public Action OnHome;
 
         private ToolbarUserButton userButton;
+        private ToolbarRulesetSelector rulesetSelector;
 
         protected override bool BlockPositionalInput => false;
 
@@ -40,7 +42,7 @@ namespace osu.Game.Overlays.Toolbar
         }
 
         [BackgroundDependencyLoader(true)]
-        private void load(OsuGame osuGame)
+        private void load(OsuGame osuGame, Bindable<RulesetInfo> parentRuleset)
         {
             Children = new Drawable[]
             {
@@ -57,7 +59,7 @@ namespace osu.Game.Overlays.Toolbar
                         {
                             Action = () => OnHome?.Invoke()
                         },
-                        new ToolbarRulesetSelector()
+                        rulesetSelector = new ToolbarRulesetSelector()
                     }
                 },
                 new FillFlowContainer
@@ -84,6 +86,9 @@ namespace osu.Game.Overlays.Toolbar
                 }
             };
 
+            // Bound after the selector is added to the hierarchy to give it a chance to load the available rulesets
+            rulesetSelector.Current.BindTo(parentRuleset);
+
             State.ValueChanged += visibility =>
             {
                 if (overlayActivationMode.Value == OverlayActivation.Disabled)
diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs
deleted file mode 100644
index 87b18ba9f4..0000000000
--- a/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using osu.Framework.Graphics.Effects;
-using osu.Game.Rulesets;
-using osuTK.Graphics;
-
-namespace osu.Game.Overlays.Toolbar
-{
-    public class ToolbarRulesetButton : ToolbarButton
-    {
-        private RulesetInfo ruleset;
-
-        public RulesetInfo Ruleset
-        {
-            get => ruleset;
-            set
-            {
-                ruleset = value;
-
-                var rInstance = ruleset.CreateInstance();
-
-                TooltipMain = rInstance.Description;
-                TooltipSub = $"Play some {rInstance.Description}";
-                SetIcon(rInstance.CreateIcon());
-            }
-        }
-
-        public bool Active
-        {
-            set
-            {
-                if (value)
-                {
-                    IconContainer.Colour = Color4.White;
-                    IconContainer.EdgeEffect = new EdgeEffectParameters
-                    {
-                        Type = EdgeEffectType.Glow,
-                        Colour = new Color4(255, 194, 224, 100),
-                        Radius = 15,
-                        Roundness = 15,
-                    };
-                }
-                else
-                {
-                    IconContainer.Colour = new Color4(255, 194, 224, 255);
-                    IconContainer.EdgeEffect = new EdgeEffectParameters();
-                }
-            }
-        }
-
-        protected override void LoadComplete()
-        {
-            base.LoadComplete();
-            IconContainer.Scale *= 1.4f;
-        }
-    }
-}
diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
index 90412ec1d1..95d7d3f73a 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
@@ -1,49 +1,41 @@
 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
 // See the LICENCE file in the repository root for full licence text.
 
-using System.Linq;
-using osu.Framework.Allocation;
-using osu.Framework.Bindables;
-using osu.Framework.Caching;
 using osu.Framework.Graphics;
 using osu.Framework.Graphics.Containers;
 using osu.Framework.Graphics.Effects;
 using osuTK;
-using osuTK.Input;
 using osuTK.Graphics;
 using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input.Events;
 using osu.Game.Rulesets;
+using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Input.Events;
+using osuTK.Input;
+using System.Linq;
+using osu.Framework.Allocation;
 
 namespace osu.Game.Overlays.Toolbar
 {
-    public class ToolbarRulesetSelector : Container
+    public class ToolbarRulesetSelector : RulesetSelector
     {
         private const float padding = 10;
 
-        private readonly FillFlowContainer modeButtons;
-        private readonly Drawable modeButtonLine;
-        private ToolbarRulesetButton activeButton;
-
-        private RulesetStore rulesets;
-        private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
+        private Drawable modeButtonLine;
 
         public ToolbarRulesetSelector()
         {
             RelativeSizeAxes = Axes.Y;
             AutoSizeAxes = Axes.X;
+        }
 
-            Children = new[]
+        [BackgroundDependencyLoader]
+        private void load()
+        {
+            AddRangeInternal(new[]
             {
-                new OpaqueBackground(),
-                modeButtons = new FillFlowContainer
+                new OpaqueBackground
                 {
-                    RelativeSizeAxes = Axes.Y,
-                    AutoSizeAxes = Axes.X,
-                    Direction = FillDirection.Horizontal,
-                    Anchor = Anchor.TopCentre,
-                    Origin = Anchor.TopCentre,
-                    Padding = new MarginPadding { Left = padding, Right = padding },
+                    Depth = 1,
                 },
                 modeButtonLine = new Container
                 {
@@ -58,36 +50,50 @@ namespace osu.Game.Overlays.Toolbar
                         Radius = 15,
                         Roundness = 15,
                     },
-                    Children = new[]
+                    Child = new Box
                     {
-                        new Box
-                        {
-                            RelativeSizeAxes = Axes.Both,
-                        }
+                        RelativeSizeAxes = Axes.Both,
                     }
                 }
-            };
+            });
         }
 
-        [BackgroundDependencyLoader]
-        private void load(RulesetStore rulesets, Bindable<RulesetInfo> parentRuleset)
+        protected override void LoadComplete()
         {
-            this.rulesets = rulesets;
+            base.LoadComplete();
 
-            foreach (var r in rulesets.AvailableRulesets)
-            {
-                modeButtons.Add(new ToolbarRulesetButton
-                {
-                    Ruleset = r,
-                    Action = delegate { ruleset.Value = r; }
-                });
-            }
-
-            ruleset.ValueChanged += rulesetChanged;
-            ruleset.DisabledChanged += disabledChanged;
-            ruleset.BindTo(parentRuleset);
+            Current.BindDisabledChanged(disabled => this.FadeColour(disabled ? Color4.Gray : Color4.White, 300), true);
+            Current.BindValueChanged(_ => moveLineToCurrent(), true);
         }
 
+        private void moveLineToCurrent()
+        {
+            foreach (TabItem<RulesetInfo> tabItem in TabContainer)
+            {
+                if (tabItem.Value == Current.Value)
+                {
+                    modeButtonLine.MoveToX(tabItem.DrawPosition.X, 200, Easing.OutQuint);
+                    break;
+                }
+            }
+        }
+
+        public override bool HandleNonPositionalInput => !Current.Disabled && base.HandleNonPositionalInput;
+
+        public override bool HandlePositionalInput => !Current.Disabled && base.HandlePositionalInput;
+
+        public override bool PropagatePositionalInputSubTree => !Current.Disabled && base.PropagatePositionalInputSubTree;
+
+        protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new ToolbarRulesetTabButton(value);
+
+        protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
+        {
+            RelativeSizeAxes = Axes.Y,
+            AutoSizeAxes = Axes.X,
+            Direction = FillDirection.Horizontal,
+            Padding = new MarginPadding { Left = padding, Right = padding },
+        };
+
         protected override bool OnKeyDown(KeyDownEvent e)
         {
             base.OnKeyDown(e);
@@ -96,46 +102,13 @@ namespace osu.Game.Overlays.Toolbar
             {
                 int requested = e.Key - Key.Number1;
 
-                RulesetInfo found = rulesets.AvailableRulesets.Skip(requested).FirstOrDefault();
+                RulesetInfo found = Rulesets.AvailableRulesets.Skip(requested).FirstOrDefault();
                 if (found != null)
-                    ruleset.Value = found;
+                    Current.Value = found;
                 return true;
             }
 
             return false;
         }
-
-        public override bool HandleNonPositionalInput => !ruleset.Disabled && base.HandleNonPositionalInput;
-        public override bool HandlePositionalInput => !ruleset.Disabled && base.HandlePositionalInput;
-
-        public override bool PropagatePositionalInputSubTree => !ruleset.Disabled && base.PropagatePositionalInputSubTree;
-
-        private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300);
-
-        private void rulesetChanged(ValueChangedEvent<RulesetInfo> e)
-        {
-            foreach (ToolbarRulesetButton m in modeButtons.Children.Cast<ToolbarRulesetButton>())
-            {
-                bool isActive = m.Ruleset.ID == e.NewValue.ID;
-                m.Active = isActive;
-                if (isActive)
-                    activeButton = m;
-            }
-
-            activeMode.Invalidate();
-        }
-
-        private Cached activeMode = new Cached();
-
-        protected override void UpdateAfterChildren()
-        {
-            base.UpdateAfterChildren();
-
-            if (!activeMode.IsValid)
-            {
-                modeButtonLine.MoveToX(activeButton.DrawPosition.X, 200, Easing.OutQuint);
-                activeMode.Validate();
-            }
-        }
     }
 }
diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs
new file mode 100644
index 0000000000..a5194ea752
--- /dev/null
+++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs
@@ -0,0 +1,76 @@
+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics.Effects;
+using osu.Framework.Graphics.UserInterface;
+using osu.Game.Rulesets;
+using osuTK.Graphics;
+using osu.Framework.Graphics;
+using osu.Framework.Input.Events;
+
+namespace osu.Game.Overlays.Toolbar
+{
+    public class ToolbarRulesetTabButton : TabItem<RulesetInfo>
+    {
+        private readonly RulesetButton ruleset;
+
+        public ToolbarRulesetTabButton(RulesetInfo value)
+            : base(value)
+        {
+            AutoSizeAxes = Axes.X;
+            RelativeSizeAxes = Axes.Y;
+            Child = ruleset = new RulesetButton
+            {
+                Active = false,
+            };
+
+            var rInstance = value.CreateInstance();
+
+            ruleset.TooltipMain = rInstance.Description;
+            ruleset.TooltipSub = $"Play some {rInstance.Description}";
+            ruleset.SetIcon(rInstance.CreateIcon());
+        }
+
+        protected override void OnActivated() => ruleset.Active = true;
+
+        protected override void OnDeactivated() => ruleset.Active = false;
+
+        private class RulesetButton : ToolbarButton
+        {
+            public bool Active
+            {
+                set
+                {
+                    if (value)
+                    {
+                        IconContainer.Colour = Color4.White;
+                        IconContainer.EdgeEffect = new EdgeEffectParameters
+                        {
+                            Type = EdgeEffectType.Glow,
+                            Colour = new Color4(255, 194, 224, 100),
+                            Radius = 15,
+                            Roundness = 15,
+                        };
+                    }
+                    else
+                    {
+                        IconContainer.Colour = new Color4(255, 194, 224, 255);
+                        IconContainer.EdgeEffect = new EdgeEffectParameters();
+                    }
+                }
+            }
+
+            protected override bool OnClick(ClickEvent e)
+            {
+                Parent.Click();
+                return base.OnClick(e);
+            }
+
+            protected override void LoadComplete()
+            {
+                base.LoadComplete();
+                IconContainer.Scale *= 1.4f;
+            }
+        }
+    }
+}
diff --git a/osu.Game/Rulesets/RulesetSelector.cs b/osu.Game/Rulesets/RulesetSelector.cs
new file mode 100644
index 0000000000..8e6ec556d2
--- /dev/null
+++ b/osu.Game/Rulesets/RulesetSelector.cs
@@ -0,0 +1,23 @@
+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Allocation;
+
+namespace osu.Game.Rulesets
+{
+    public abstract class RulesetSelector : TabControl<RulesetInfo>
+    {
+        [Resolved]
+        protected RulesetStore Rulesets { get; private set; }
+
+        protected override Dropdown<RulesetInfo> CreateDropdown() => null;
+
+        [BackgroundDependencyLoader]
+        private void load()
+        {
+            foreach (var r in Rulesets.AvailableRulesets)
+                AddItem(r);
+        }
+    }
+}
diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs
index a80056d164..b7fb758298 100644
--- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs
+++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs
@@ -195,6 +195,7 @@ namespace osu.Game.Screens.Multi.Match
 
             Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
             Mods.Value = e.NewValue?.RequiredMods?.ToArray() ?? Array.Empty<Mod>();
+
             if (e.NewValue?.Ruleset != null)
                 Ruleset.Value = e.NewValue.Ruleset;
         }