diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Uns/UnsTree.razor b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Uns/UnsTree.razor
new file mode 100644
index 00000000..d0f08156
--- /dev/null
+++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Uns/UnsTree.razor
@@ -0,0 +1,142 @@
+@* Recursive, read-only UNS browse-tree renderer. Modelled on DriverBrowseTree:
+ per-node indent, expand chevron and Bootstrap/theme styling. This component
+ owns no state and calls no service — it reads node.Expanded/Loading/Error/
+ Children and raises callbacks; the host page owns expansion + lazy loading. *@
+@using ZB.MOM.WW.OtOpcUa.AdminUI.Uns
+
+@foreach (var root in Roots)
+{
+ @RenderNode(root, 0)
+}
+
+@code {
+ /// The top-level UNS nodes to render (typically the enterprise roots).
+ [Parameter, EditorRequired] public IReadOnlyList Roots { get; set; } = default!;
+
+ /// Raised for the primary "add child" action of a node (e.g. "+ Area" on a cluster, "+ Tag" on equipment).
+ [Parameter] public EventCallback OnAddChild { get; set; }
+
+ /// Raised for the equipment-only "+ Virtual tag" action, kept distinct from OnAddChild ("+ Tag").
+ [Parameter] public EventCallback OnAddVirtualTag { get; set; }
+
+ /// Raised when the user edits a node (Area/Line/Equipment/Tag/VirtualTag).
+ [Parameter] public EventCallback OnEdit { get; set; }
+
+ /// Raised when the user deletes a node (Area/Line/Equipment/Tag/VirtualTag).
+ [Parameter] public EventCallback OnDelete { get; set; }
+
+ /// Raised when the user toggles a node's expansion; the host owns the resulting state + lazy load.
+ [Parameter] public EventCallback OnToggleExpand { get; set; }
+
+ /// Optional case-insensitive substring filter applied to visible children by DisplayName.
+ [Parameter] public string? Filter { get; set; }
+
+ private RenderFragment RenderNode(UnsNode node, int depth) => __builder =>
+ {
+ var indent = $"padding-left:{depth * 18}px";
+ var hasExpander = node.Children.Count > 0 || node.HasLazyChildren;
+