diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/ValidationMessageTagHelper.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/ValidationMessageTagHelper.cs
new file mode 100644
index 0000000000..e10d61183d
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.TagHelpers/ValidationMessageTagHelper.cs
@@ -0,0 +1,55 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNet.Mvc.Rendering;
+using Microsoft.AspNet.Razor.Runtime.TagHelpers;
+using Microsoft.AspNet.Razor.TagHelpers;
+
+namespace Microsoft.AspNet.Mvc.TagHelpers
+{
+ ///
+ /// implementation targeting <span> elements with validation-for attributes.
+ ///
+ [TagName("span")]
+ [ContentBehavior(ContentBehavior.Modify)]
+ public class ValidationMessageTagHelper : TagHelper
+ {
+ [Activate]
+ private ViewContext ViewContext { get; set; }
+
+ [Activate]
+ private IHtmlGenerator Generator { get; set; }
+
+ ///
+ /// Name to be validated on the current model.
+ ///
+ [HtmlAttributeName("validation-for")]
+ public ModelExpression For { get; set; }
+
+ ///
+ public override void Process(TagHelperContext context, TagHelperOutput output)
+ {
+ if (For != null)
+ {
+ var tagBuilder = Generator.GenerateValidationMessage(ViewContext,
+ For.Name,
+ message: null,
+ tag: null,
+ htmlAttributes: null);
+
+ if (tagBuilder != null)
+ {
+ output.MergeAttributes(tagBuilder);
+
+ // We check for whitespace to detect scenarios such as:
+ //
+ //
+ if (string.IsNullOrWhiteSpace(output.Content))
+ {
+ output.Content = tagBuilder.InnerHtml;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ValidationMessageTagHelperTest.cs b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ValidationMessageTagHelperTest.cs
new file mode 100644
index 0000000000..dbd8af09b6
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ValidationMessageTagHelperTest.cs
@@ -0,0 +1,211 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Threading.Tasks;
+using Microsoft.AspNet.Http;
+using Microsoft.AspNet.Mvc.ModelBinding;
+using Microsoft.AspNet.Mvc.Razor;
+using Microsoft.AspNet.Mvc.Rendering;
+using Microsoft.AspNet.Razor.Runtime.TagHelpers;
+using Microsoft.AspNet.Routing;
+using Moq;
+using Xunit;
+
+namespace Microsoft.AspNet.Mvc.TagHelpers
+{
+ public class ValidationMessageTagHelperTest
+ {
+ [Fact]
+ public async Task ProcessAsync_GeneratesExpectedOutput()
+ {
+ // Arrange
+ var metadataProvider = new DataAnnotationsModelMetadataProvider();
+ var modelExpression = CreateModelExpression("Name");
+ var validationMessageTagHelper = new ValidationMessageTagHelper
+ {
+ For = modelExpression
+ };
+
+ var tagHelperContext = new TagHelperContext(
+ allAttributes: new Dictionary
+ {
+ { "id", "myvalidationmessage" },
+ { "for", modelExpression },
+ });
+ var output = new TagHelperOutput(
+ "original tag name",
+ attributes: new Dictionary
+ {
+ { "id", "myvalidationmessage" }
+ },
+ content: "Something");
+ var htmlGenerator = new TestableHtmlGenerator(metadataProvider);
+ var viewContext = TestableHtmlGenerator.GetViewContext(model: null,
+ htmlGenerator: htmlGenerator,
+ metadataProvider: metadataProvider);
+
+ var activator = new DefaultTagHelperActivator();
+ activator.Activate(validationMessageTagHelper, viewContext);
+
+ // Act
+ await validationMessageTagHelper.ProcessAsync(tagHelperContext, output);
+
+ // Assert
+ Assert.Equal(4, output.Attributes.Count);
+ var attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("id"));
+ Assert.Equal("myvalidationmessage", attribute.Value);
+ attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("class"));
+ Assert.Equal("field-validation-valid", attribute.Value);
+ attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("data-valmsg-for"));
+ Assert.Equal("Name", attribute.Value);
+ attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("data-valmsg-replace"));
+ Assert.Equal("true", attribute.Value);
+ Assert.Equal("Something", output.Content);
+ Assert.Equal("original tag name", output.TagName);
+ }
+
+ [Fact]
+ public async Task ProcessAsync_CallsIntoGenerateValidationMessageWithExpectedParameters()
+ {
+ // Arrange
+ var validationMessageTagHelper = new ValidationMessageTagHelper
+ {
+ For = CreateModelExpression("Hello")
+ };
+ var output = new TagHelperOutput(
+ "span",
+ attributes: new Dictionary(),
+ content: "Content of validation message");
+ var expectedViewContext = CreateViewContext();
+ var generator = new Mock();
+ generator
+ .Setup(mock =>
+ mock.GenerateValidationMessage(expectedViewContext, "Hello", null, null, null))
+ .Returns(new TagBuilder("span"))
+ .Verifiable();
+
+ SetViewContextAndGenerator(validationMessageTagHelper, expectedViewContext, generator.Object);
+
+ // Act & Assert
+ await validationMessageTagHelper.ProcessAsync(context: null, output: output);
+
+ generator.Verify();
+ Assert.Equal("span", output.TagName);
+ Assert.Empty(output.Attributes);
+ Assert.Equal("Content of validation message", output.Content);
+ }
+
+ [Theory]
+ [InlineData("Content of validation message", "Content of validation message")]
+ [InlineData("\r\n \r\n", "New HTML")]
+ public async Task ProcessAsync_MergesTagBuilderFromGenerateValidationMessage(
+ string outputContent, string expectedOutputContent)
+ {
+ // Arrange
+ var validationMessageTagHelper = new ValidationMessageTagHelper
+ {
+ For = CreateModelExpression("Hello")
+ };
+ var output = new TagHelperOutput(
+ "span",
+ attributes: new Dictionary(),
+ content: outputContent);
+ var tagBuilder = new TagBuilder("span2")
+ {
+ InnerHtml = "New HTML"
+ };
+ tagBuilder.Attributes.Add("data-foo", "bar");
+ tagBuilder.Attributes.Add("data-hello", "world");
+
+ var expectedViewContext = CreateViewContext();
+ var generator = new Mock(MockBehavior.Strict);
+ var setup = generator
+ .Setup(mock => mock.GenerateValidationMessage(
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny