diff --git a/src/Components/Web/src/Forms/InputHidden.cs b/src/Components/Web/src/Forms/InputHidden.cs
new file mode 100644
index 000000000000..d2f95d1b6272
--- /dev/null
+++ b/src/Components/Web/src/Forms/InputHidden.cs
@@ -0,0 +1,44 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.AspNetCore.Components.Rendering;
+
+namespace Microsoft.AspNetCore.Components.Forms;
+
+///
+/// An hidden input component for storing values.
+///
+public class InputHidden : InputBase
+{
+ ///
+ /// Gets or sets the associated .
+ ///
+ /// May be if accessed before the component is rendered.
+ ///
+ ///
+ [DisallowNull] public ElementReference? Element { get; protected set; }
+
+ ///
+ protected override void BuildRenderTree(RenderTreeBuilder builder)
+ {
+ builder.OpenElement(0, "input");
+ builder.AddAttribute(1, "type", "hidden");
+ builder.AddMultipleAttributes(2, AdditionalAttributes);
+ builder.AddAttributeIfNotNullOrEmpty(3, "name", NameAttributeValue);
+ builder.AddAttributeIfNotNullOrEmpty(4, "class", CssClass);
+ builder.AddAttribute(5, "value", CurrentValueAsString);
+ builder.AddAttribute(6, "onchange", EventCallback.Factory.CreateBinder(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
+ builder.SetUpdatesAttributeName("value");
+ builder.AddElementReferenceCapture(7, __inputReference => Element = __inputReference);
+ builder.CloseElement();
+ }
+
+ ///
+ protected override bool TryParseValueFromString(string? value, out string? result, [NotNullWhen(false)] out string? validationErrorMessage)
+ {
+ result = value;
+ validationErrorMessage = null;
+ return true;
+ }
+}
diff --git a/src/Components/Web/src/PublicAPI.Unshipped.txt b/src/Components/Web/src/PublicAPI.Unshipped.txt
index 99365e10804e..5b85eaf45fdc 100644
--- a/src/Components/Web/src/PublicAPI.Unshipped.txt
+++ b/src/Components/Web/src/PublicAPI.Unshipped.txt
@@ -1,3 +1,9 @@
#nullable enable
+Microsoft.AspNetCore.Components.Forms.InputHidden
+Microsoft.AspNetCore.Components.Forms.InputHidden.Element.get -> Microsoft.AspNetCore.Components.ElementReference?
+Microsoft.AspNetCore.Components.Forms.InputHidden.Element.set -> void
+Microsoft.AspNetCore.Components.Forms.InputHidden.InputHidden() -> void
Microsoft.AspNetCore.Components.Web.Internal.IInternalWebJSInProcessRuntime.InvokeJS(in Microsoft.JSInterop.Infrastructure.JSInvocationInfo invocationInfo) -> string!
+override Microsoft.AspNetCore.Components.Forms.InputHidden.BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder! builder) -> void
+override Microsoft.AspNetCore.Components.Forms.InputHidden.TryParseValueFromString(string? value, out string? result, out string? validationErrorMessage) -> bool
virtual Microsoft.AspNetCore.Components.Routing.NavLink.ShouldMatch(string! uriAbsolute) -> bool
\ No newline at end of file
diff --git a/src/Components/Web/test/Forms/InputHiddenTest.cs b/src/Components/Web/test/Forms/InputHiddenTest.cs
new file mode 100644
index 000000000000..73cf7d5414f8
--- /dev/null
+++ b/src/Components/Web/test/Forms/InputHiddenTest.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.Components.Forms;
+
+public class InputHiddenTest
+{
+ [Fact]
+ public async Task InputElementIsAssignedSuccessfully()
+ {
+ // Arrange
+ var model = new TestModel();
+ var rootComponent = new TestInputHostComponent
+ {
+ EditContext = new EditContext(model),
+ ValueExpression = () => model.StringProperty,
+ };
+
+ // Act
+ var inputHiddenComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
+
+ // Assert
+ Assert.NotNull(inputHiddenComponent.Element);
+ }
+
+ private class TestModel
+ {
+ public string StringProperty { get; set; }
+ }
+}
diff --git a/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs b/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs
index dce828fcefbf..b6948f9b766c 100644
--- a/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs
+++ b/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs
@@ -104,6 +104,23 @@ public void CanBindParameterToTheDefaultForm(bool suppressEnhancedNavigation)
DispatchToFormCore(dispatchToForm);
}
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void InputHiddenCanStoreData(bool suppressEnhancedNavigation)
+ {
+ var dispatchToForm = new DispatchToForm(this)
+ {
+ Url = "forms/default-form-input-hidden",
+ FormCssSelector = "form",
+ SuppressEnhancedNavigation = suppressEnhancedNavigation,
+ };
+ DispatchToFormCore(dispatchToForm);
+
+ Browser.Equal("stranger", () => Browser.Exists(By.Id("hidden")).GetDomProperty("value"));
+ Browser.Equal("Hello stranger!", () => Browser.Exists(By.Id("pass")).Text);
+ }
+
[Theory]
[InlineData(true)]
[InlineData(false)]
diff --git a/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/Forms/DefaultFormInputHidden.razor b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/Forms/DefaultFormInputHidden.razor
new file mode 100644
index 000000000000..75107fb33e9b
--- /dev/null
+++ b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/Forms/DefaultFormInputHidden.razor
@@ -0,0 +1,21 @@
+@page "/forms/default-form-input-hidden"
+@page "/reexecution/forms/default-form-input-hidden"
+@using Microsoft.AspNetCore.Components.Forms
+
+Default form Input Hidden
+
+
+
+
+
+
+@if (_submitted)
+{
+ Hello @Parameter!
+}
+
+@code {
+ bool _submitted = false;
+
+ [SupplyParameterFromForm] public string Parameter { get; set; } = "stranger";
+}