diff --git a/src/LibraryManager.Vsix/Json/Completion/FileMappingRootCompletionProvider.cs b/src/LibraryManager.Vsix/Json/Completion/FileMappingRootCompletionProvider.cs
new file mode 100644
index 00000000..5be8341c
--- /dev/null
+++ b/src/LibraryManager.Vsix/Json/Completion/FileMappingRootCompletionProvider.cs
@@ -0,0 +1,111 @@
+// 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.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.Imaging;
+using Microsoft.VisualStudio.Utilities;
+using Microsoft.Web.LibraryManager.Contracts;
+using Microsoft.Web.LibraryManager.Vsix.Contracts;
+using Microsoft.WebTools.Languages.Json.Editor.Completion;
+using Microsoft.WebTools.Languages.Json.Parser.Nodes;
+
+namespace Microsoft.Web.LibraryManager.Vsix.Json.Completion
+{
+    [Export(typeof(IJsonCompletionListProvider))]
+    [Name(nameof(FileMappingRootCompletionProvider))]
+    internal class FileMappingRootCompletionProvider : BaseCompletionProvider
+    {
+        private readonly IDependenciesFactory _dependenciesFactory;
+
+        [ImportingConstructor]
+        internal FileMappingRootCompletionProvider(IDependenciesFactory dependenciesFactory)
+        {
+            _dependenciesFactory = dependenciesFactory;
+        }
+
+        public override JsonCompletionContextType ContextType => JsonCompletionContextType.PropertyValue;
+
+        [SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Checked completion first")]
+        protected override IEnumerable<JsonCompletionEntry> GetEntries(JsonCompletionContext context)
+        {
+            MemberNode member = context.ContextNode.FindType<MemberNode>();
+
+            // This provides completions for libraries/[n]/fileMappings/[m]/root
+            if (member == null || member.UnquotedNameText != ManifestConstants.Root)
+                yield break;
+
+            MemberNode possibleFileMappingsNode = member.Parent.FindType<MemberNode>();
+            bool isInFileMapping = possibleFileMappingsNode?.UnquotedNameText == ManifestConstants.FileMappings;
+            if (!isInFileMapping)
+                yield break;
+
+            ObjectNode parent = possibleFileMappingsNode.Parent as ObjectNode;
+
+            if (!JsonHelpers.TryGetInstallationState(parent, out ILibraryInstallationState state))
+                yield break;
+
+            if (string.IsNullOrEmpty(state.Name))
+                yield break;
+
+            IDependencies dependencies = _dependenciesFactory.FromConfigFile(ConfigFilePath);
+            IProvider provider = dependencies.GetProvider(state.ProviderId);
+            ILibraryCatalog catalog = provider?.GetCatalog();
+
+            if (catalog is null)
+            {
+                yield break;
+            }
+
+            Task<ILibrary> task = catalog.GetLibraryAsync(state.Name, state.Version, CancellationToken.None);
+
+            if (task.IsCompleted)
+            {
+                if (task.Result is ILibrary library)
+                {
+                    foreach (JsonCompletionEntry item in GetRootCompletions(context, library))
+                    {
+                        yield return item;
+                    }
+                }
+            }
+            else
+            {
+                yield return new SimpleCompletionEntry(Resources.Text.Loading, string.Empty, KnownMonikers.Loading, context.Session);
+                _ = task.ContinueWith(async (t) =>
+                {
+                    await VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+
+                    if (!(t.Result is ILibrary library))
+                        return;
+
+                    if (!context.Session.IsDismissed)
+                    {
+                        IEnumerable<JsonCompletionEntry> completions = GetRootCompletions(context, library);
+
+                        UpdateListEntriesSync(context, completions);
+                    }
+                }, TaskScheduler.Default);
+            }
+        }
+
+        private IEnumerable<JsonCompletionEntry> GetRootCompletions(JsonCompletionContext context, ILibrary library)
+        {
+            HashSet<string> libraryFolders = [];
+            foreach (string file in library.Files.Keys)
+            {
+                int sepIndex = file.LastIndexOf('/');
+                if (sepIndex >= 0)
+                {
+                    libraryFolders.Add(file.Substring(0, file.LastIndexOf('/')));
+                }
+            }
+
+            return libraryFolders.Select(folder => new SimpleCompletionEntry(folder, KnownMonikers.FolderClosed, context.Session));
+        }
+    }
+}
diff --git a/src/LibraryManager.Vsix/Json/Completion/FilesCompletionProvider.cs b/src/LibraryManager.Vsix/Json/Completion/FilesCompletionProvider.cs
index 25f1991f..4e6d8983 100644
--- a/src/LibraryManager.Vsix/Json/Completion/FilesCompletionProvider.cs
+++ b/src/LibraryManager.Vsix/Json/Completion/FilesCompletionProvider.cs
@@ -1,6 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System;
 using System.Collections.Generic;
 using System.ComponentModel.Composition;
 using System.Diagnostics.CodeAnalysis;
@@ -9,7 +10,6 @@
 using System.Threading.Tasks;
 using System.Windows;
 using System.Windows.Data;
-using System.Windows.Media;
 using Microsoft.VisualStudio.Imaging;
 using Microsoft.VisualStudio.Imaging.Interop;
 using Microsoft.VisualStudio.PlatformUI;
@@ -19,6 +19,7 @@
 using Microsoft.Web.LibraryManager.Vsix.Shared;
 using Microsoft.WebTools.Languages.Json.Editor.Completion;
 using Microsoft.WebTools.Languages.Json.Parser.Nodes;
+using Microsoft.WebTools.Languages.Shared.Parser;
 using Microsoft.WebTools.Languages.Shared.Parser.Nodes;
 
 namespace Microsoft.Web.LibraryManager.Vsix.Json.Completion
@@ -45,10 +46,20 @@ protected override IEnumerable<JsonCompletionEntry> GetEntries(JsonCompletionCon
         {
             MemberNode member = context.ContextNode.FindType<MemberNode>();
 
+            // We can show completions for "files".  This could be libraries/[n]/files or
+            // libraries/[n]/fileMappings/[m]/files.
             if (member == null || member.UnquotedNameText != "files")
                 yield break;
 
-            var parent = member.Parent as ObjectNode;
+            // If the current member is "files", then it is either:
+            // - a library "files" property
+            // - a fileMapping "files" property
+            MemberNode possibleFileMappingsNode = member.Parent.FindType<MemberNode>();
+            bool isFileMapping = possibleFileMappingsNode?.UnquotedNameText == "fileMappings";
+
+            ObjectNode parent = isFileMapping
+                ? possibleFileMappingsNode.Parent as ObjectNode
+                : member.Parent as ObjectNode;
 
             if (!JsonHelpers.TryGetInstallationState(parent, out ILibraryInstallationState state))
                 yield break;
@@ -67,18 +78,23 @@ protected override IEnumerable<JsonCompletionEntry> GetEntries(JsonCompletionCon
             FrameworkElement presenter = GetPresenter(context);
             IEnumerable<string> usedFiles = GetUsedFiles(context);
 
+            string rootPathPrefix = isFileMapping ? GetRootValue(member) : string.Empty;
+            static string GetRootValue(MemberNode fileMappingNode)
+            {
+                FindFileMappingRootVisitor visitor = new FindFileMappingRootVisitor();
+                fileMappingNode.Parent?.Accept(visitor);
+                return visitor.FoundNode?.UnquotedValueText ?? string.Empty;
+            }
+
             if (task.IsCompleted)
             {
                 if (!(task.Result is ILibrary library))
                     yield break;
 
-                foreach (string file in library.Files.Keys)
+                IEnumerable<JsonCompletionEntry> completions = GetFileCompletions(context, usedFiles, library, rootPathPrefix);
+                foreach (JsonCompletionEntry item in completions)
                 {
-                    if (!usedFiles.Contains(file))
-                    {
-                        ImageMoniker glyph = WpfUtil.GetImageMonikerForFile(file);
-                        yield return new SimpleCompletionEntry(file, glyph, context.Session);
-                    }
+                    yield return item;
                 }
             }
             else
@@ -94,23 +110,40 @@ protected override IEnumerable<JsonCompletionEntry> GetEntries(JsonCompletionCon
 
                     if (!context.Session.IsDismissed)
                     {
-                        var results = new List<JsonCompletionEntry>();
-
-                        foreach (string file in library.Files.Keys)
-                        {
-                            if (!usedFiles.Contains(file))
-                            {
-                                ImageMoniker glyph = WpfUtil.GetImageMonikerForFile(file);
-                                results.Add(new SimpleCompletionEntry(file, glyph, context.Session));
-                            }
-                        }
-
-                        UpdateListEntriesSync(context, results);
+                        IEnumerable<JsonCompletionEntry> completions = GetFileCompletions(context, usedFiles, library, rootPathPrefix);
+
+                        UpdateListEntriesSync(context, completions);
                     }
                 }, TaskScheduler.Default);
             }
         }
 
+        private static IEnumerable<JsonCompletionEntry> GetFileCompletions(JsonCompletionContext context, IEnumerable<string> usedFiles, ILibrary library, string root)
+        {
+            static bool alwaysInclude(string s) => true;
+            bool includeIfUnderRoot(string s) => FileHelpers.IsUnderRootDirectory(s, root);
+
+            Func<string, bool> filter = string.IsNullOrEmpty(root)
+                ? alwaysInclude
+                : includeIfUnderRoot;
+
+            bool rootHasTrailingSlash = string.IsNullOrEmpty(root) || root.EndsWith("/") || root.EndsWith("\\");
+            int nameOffset = rootHasTrailingSlash ? root.Length : root.Length + 1;
+
+            foreach (string file in library.Files.Keys)
+            {
+                if (filter(file))
+                {
+                    string fileSubPath = file.Substring(nameOffset);
+                    if (!usedFiles.Contains(fileSubPath))
+                    {
+                        ImageMoniker glyph = WpfUtil.GetImageMonikerForFile(file);
+                        yield return new SimpleCompletionEntry(fileSubPath, glyph, context.Session);
+                    }
+                }
+            }
+        }
+
         private static IEnumerable<string> GetUsedFiles(JsonCompletionContext context)
         {
             ArrayNode array = context.ContextNode.FindType<ArrayNode>();
@@ -139,5 +172,31 @@ private FrameworkElement GetPresenter(JsonCompletionContext context)
 
             return presenter;
         }
+
+        private class FindFileMappingRootVisitor : INodeVisitor
+        {
+            public MemberNode FoundNode { get; private set; }
+
+            public VisitNodeResult Visit(Node node)
+            {
+                if (node is ObjectNode)
+                {
+                    return VisitNodeResult.Continue;
+                }
+                // we only look at the object and it's members, this is not a recursive search
+                if (node is not MemberNode mn)
+                {
+                    return VisitNodeResult.SkipChildren;
+                }
+
+                if (mn.UnquotedNameText == ManifestConstants.Root)
+                {
+                    FoundNode = mn;
+                    return VisitNodeResult.Cancel;
+                }
+
+                return VisitNodeResult.SkipChildren;
+            }
+        }
     }
 }
diff --git a/src/LibraryManager.Vsix/Microsoft.Web.LibraryManager.Vsix.csproj b/src/LibraryManager.Vsix/Microsoft.Web.LibraryManager.Vsix.csproj
index 74f2ebe3..35bd8227 100644
--- a/src/LibraryManager.Vsix/Microsoft.Web.LibraryManager.Vsix.csproj
+++ b/src/LibraryManager.Vsix/Microsoft.Web.LibraryManager.Vsix.csproj
@@ -48,6 +48,7 @@
     <Compile Include="Commands\InstallLibraryCommand.cs" />
     <Compile Include="Contracts\DependenciesFactory.cs" />
     <Compile Include="Contracts\IDependenciesFactory.cs" />
+    <Compile Include="Json\Completion\FileMappingRootCompletionProvider.cs" />
     <Compile Include="Search\ISearchService.cs" />
     <Compile Include="Search\LocationSearchService.cs" />
     <Compile Include="Search\ProviderCatalogSearchService.cs" />
@@ -417,4 +418,4 @@
       <VSIXSourceItem Remove="@(_VsixSourceItemsFromNuGet)" />
     </ItemGroup>
   </Target>
-</Project>
+</Project>
\ No newline at end of file