diff --git a/src/Microsoft.AspNetCore.Mvc.TagHelpers/ButtonTagHelper.cs b/src/Microsoft.AspNetCore.Mvc.TagHelpers/ButtonTagHelper.cs
new file mode 100644
index 0000000000..7fc14aead7
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.TagHelpers/ButtonTagHelper.cs
@@ -0,0 +1,185 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using Microsoft.AspNetCore.Mvc.Rendering;
+using Microsoft.AspNetCore.Mvc.Routing;
+using Microsoft.AspNetCore.Mvc.ViewFeatures;
+using Microsoft.AspNetCore.Razor.TagHelpers;
+using Microsoft.AspNetCore.Routing;
+
+namespace Microsoft.AspNetCore.Mvc.TagHelpers
+{
+ ///
+ /// implementation targeting <button> elements.
+ ///
+ [HtmlTargetElement("button", Attributes = ActionAttributeName)]
+ [HtmlTargetElement("button", Attributes = ControllerAttributeName)]
+ [HtmlTargetElement("button", Attributes = AreaAttributeName)]
+ [HtmlTargetElement("button", Attributes = RouteAttributeName)]
+ [HtmlTargetElement("button", Attributes = RouteValuesDictionaryName)]
+ [HtmlTargetElement("button", Attributes = RouteValuesPrefix + "*")]
+ public class ButtonTagHelper : TagHelper
+ {
+ private const string ActionAttributeName = "asp-action";
+ private const string ControllerAttributeName = "asp-controller";
+ private const string AreaAttributeName = "asp-area";
+ private const string RouteAttributeName = "asp-route";
+ private const string RouteValuesDictionaryName = "asp-all-route-data";
+ private const string RouteValuesPrefix = "asp-route-";
+ private const string FormAction = "formaction";
+ private IDictionary _routeValues;
+
+ ///
+ /// Creates a new .
+ ///
+ /// The .
+ public ButtonTagHelper(IUrlHelperFactory urlHelperFactory)
+ {
+ UrlHelperFactory = urlHelperFactory;
+ }
+
+ ///
+ public override int Order => -1000;
+
+ ///
+ /// Gets or sets the for the current request.
+ ///
+ [HtmlAttributeNotBound]
+ [ViewContext]
+ public ViewContext ViewContext { get; set; }
+
+ protected IUrlHelperFactory UrlHelperFactory { get; }
+
+ ///
+ /// The name of the action method.
+ ///
+ [HtmlAttributeName(ActionAttributeName)]
+ public string Action { get; set; }
+
+ ///
+ /// The name of the controller.
+ ///
+ [HtmlAttributeName(ControllerAttributeName)]
+ public string Controller { get; set; }
+
+ ///
+ /// The name of the area.
+ ///
+ [HtmlAttributeName(AreaAttributeName)]
+ public string Area { get; set; }
+
+ ///
+ /// Name of the route.
+ ///
+ ///
+ /// Must be null if or is non-null.
+ ///
+ [HtmlAttributeName(RouteAttributeName)]
+ public string Route { get; set; }
+
+ ///
+ /// Additional parameters for the route.
+ ///
+ [HtmlAttributeName(RouteValuesDictionaryName, DictionaryAttributePrefix = RouteValuesPrefix)]
+ public IDictionary RouteValues
+ {
+ get
+ {
+ if (_routeValues == null)
+ {
+ _routeValues = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ }
+
+ return _routeValues;
+ }
+ set
+ {
+ _routeValues = value;
+ }
+ }
+
+ ///
+ /// Does nothing if user provides an formaction attribute.
+ ///
+ /// Thrown if formaction attribute is provided and , ,
+ /// or are non-null or if the user provided asp-route-* attributes.
+ /// Also thrown if and one or both of and
+ /// are non-null
+ ///
+ public override void Process(TagHelperContext context, TagHelperOutput output)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (output == null)
+ {
+ throw new ArgumentNullException(nameof(output));
+ }
+
+ // If "formaction" is already set, it means the user is attempting to use a normal button.
+ if (output.Attributes.ContainsName(FormAction))
+ {
+ if (Action != null || Controller != null || Area != null || Route != null || RouteValues.Count != 0)
+ {
+ // User specified a formaction and one of the bound attributes; can't determine the formaction attribute.
+ throw new InvalidOperationException(
+ Resources.FormatButtonTagHelper_CannotOverrideFormAction(
+ "