diff --git a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs
index 6097208bee..c887990cdb 100644
--- a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs
+++ b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs
@@ -151,6 +151,13 @@ namespace Microsoft.AspNetCore.Components
public string __internalId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
+ public readonly partial struct ElementReference
+ {
+ private readonly object _dummy;
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
+ public string __internalId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
+ }
+ [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct EventCallback
{
private readonly object _dummy;
diff --git a/src/Components/Components/src/ElementReference.cs b/src/Components/Components/src/ElementReference.cs
new file mode 100644
index 0000000000..be85bffab0
--- /dev/null
+++ b/src/Components/Components/src/ElementReference.cs
@@ -0,0 +1,58 @@
+// 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.ComponentModel;
+using System.Threading;
+
+namespace Microsoft.AspNetCore.Components
+{
+ ///
+ /// Represents a reference to a rendered element.
+ ///
+ public readonly struct ElementReference
+ {
+ private static long _nextIdForWebAssemblyOnly = 1;
+
+ ///
+ /// Gets a unique identifier for .
+ ///
+ ///
+ /// The Id is unique at least within the scope of a given user/circuit.
+ /// This property is public to support Json serialization and should not be used by user code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string __internalId { get; }
+
+ internal string Id => __internalId;
+
+ private ElementReference(string id)
+ {
+ __internalId = id;
+ }
+
+ internal static ElementReference CreateWithUniqueId()
+ => new ElementReference(CreateUniqueId());
+
+ private static string CreateUniqueId()
+ {
+ if (PlatformInfo.IsWebAssembly)
+ {
+ // On WebAssembly there's only one user, so it's fine to expose the number
+ // of IDs that have been assigned, and this is cheaper than creating a GUID.
+ // It's unfortunate that this still involves a heap allocation. If that becomes
+ // a problem we could extend RenderTreeFrame to have both "string" and "long"
+ // fields for ElementRefCaptureId, of which only one would be in use depending
+ // on the platform.
+ var id = Interlocked.Increment(ref _nextIdForWebAssemblyOnly);
+ return id.ToString();
+ }
+ else
+ {
+ // For remote rendering, it's important not to disclose any cross-user state,
+ // such as the number of IDs that have been assigned.
+ return Guid.NewGuid().ToString("D");
+ }
+ }
+ }
+}